一个用户对应一个购物车(业务场景)
1: 表设计
person(照图)
car表中的字段不能与person表中的字段重复,如果重复了写复杂sql时需给重复的字段起别名,为了减少sql编写的复杂,所以确保两张表之间的字段唯一性
DROP TABLE IF EXISTS `car`;
CREATE TABLE `car` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(20) DEFAULT NULL,
`pid` int(11) NOT NULL,
PRIMARY KEY (`cid`),
KEY `cp` (`pid`),
CONSTRAINT `cp` FOREIGN KEY (`pid`) REFERENCES `person` (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `car` VALUES (1, '忆柳的购物车', 2);
INSERT INTO `car` VALUES (2, '梦琪的购物车', 1);
INSERT INTO `car` VALUES (3, '新柔的购物车', 5);
INSERT INTO `car` VALUES (4, '慕青的购物车', 3);
INSERT INTO `car` VALUES (5, '初夏的购物车', 4);
2: 根据表创建实体对象
public class Car implements Serializable {
private int cid;
private String cname;
private int pid;
// get/set...
}
public class Person implements Serializable {
private int id;
private String name;
private Date bir;
private String address;
private Car car;// 用户拥有购物车
}
3:构建mapper配置文件
<mapper namespace="beike.mybatis.mapper.PersonMapper">
<resultMap type="Person" id="PersonCarResultMap">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="bir" column="bir"/>
<association property="car" javaType="Car">
<id property="cid" column="cid">id>
<result property="cname" column="cname">result>
<result property="pid" column="pid">result>
association>
resultMap>
<select id="getPersonCar" resultMap="PersonCarResultMap">
SELECT * FROM person,car
WHERE person.Id=car.pid
select>
mapper>
Car的映射关系写在自已的CarMapper.xml中,在PersonMapper.xml使用association引用
CarMapper.xml,添加接口CarMapper.java
<mapper namespace="beike.mybatis.mapper.CarMapper">
<resultMap type="Car" id="CarResultMap">
<id property="cid" column="cid">id>
<result property="cname" column="cname">result>
<result property="pid" column="pid">result>
resultMap>
mapper>
PersonMapper.xml
<mapper namespace="beike.mybatis.mapper.PersonMapper">
<resultMap type="Person" id="PersonCarResultMap">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="bir" column="bir"/>
<association property="car" javaType="Car" resultMap="beike.mybatis.mapper.CarMapper.CarResultMap">
association>
resultMap>
<select id="getPersonCar" resultMap="PersonCarResultMap">
SELECT * FROM person,car
WHERE person.Id=car.pid
select>
mybatis-config.xml
<mappers>
<package name="beike.mybatis.mapper"/>
mappers>
注意:
第一种关联和第二种关联我们需要写出的是复杂查询的语句
如果是第三种关联查询,我们可以将复杂查询拆分成简单查询
示例
CarMapper
public interface CarMapper {
Car getCar(int pid);
}
<mapper namespace="beike.mybatis.mapper.CarMapper">
<resultMap type="Car" id="CarResultMap">
<id property="cid" column="cid">id>
<result property="cname" column="cname">result>
<result property="pid" column="pid">result>
resultMap>
<select id="getCar" resultMap="CarResultMap">
SELECT * FROM car WHERE pid=#{pid}
select>
mapper>
PersonMapper
//查询
public List<Person> getPersonCar();
public Person getById(int id);
<mapper namespace="beike.mybatis.mapper.PersonMapper">
<resultMap type="Person" id="PersonCarResultMap">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="bir" column="bir"/>
<association property="car" column="id" javaType="Car" select="beike.mybatis.mapper.CarMapper.getCar">
association>
resultMap>
<select id="getPersonCar" resultMap="PersonCarResultMap">
SELECT * FROM person
select>
<select id="getById" resultMap="PersonCarResultMap">
SELECT * FROM person WHERE id=#{id}
select>
mapper>
测试类
@Test
public void testGetPersonCar() {
//定义配置文件路径
String fileName="mybatis-config.xml";
try {
//通过配置文件创建输入流对象
InputStream inputStream = Resources.getResourceAsStream(fileName);
//读取配置文件 实例化我们工厂
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//通过反射机制获取接口对象
PersonMapper mapper = session.getMapper(PersonMapper.class);
//调用接口方法
List<Person> list = mapper.getPersonCar();
//关闭
session.close();
System.out.println(list);
} catch (IOException e) {
e.printStackTrace();
}
}
建模(分析业务)一个用户对应多个订单
1: 建表 person orders(orders外键关联person表主键)
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`oid` int(11) NOT NULL AUTO_INCREMENT,
`oname` varchar(255) DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`oid`),
KEY `op` (`pid`),
CONSTRAINT `op` FOREIGN KEY (`pid`) REFERENCES `person` (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;
INSERT INTO `order` VALUES (1, '荣耀MagicBook 2019 14英寸轻薄窄边框', 2);
INSERT INTO `order` VALUES (2, '小米 (MI)Ruby 2019款 15.6英寸金属轻薄', 1);
INSERT INTO `order` VALUES (3, '戴尔灵越14 燃 14英寸英特尔酷睿i5轻薄窄边框', 3);
INSERT INTO `order` VALUES (4, '联想(Lenovo)小新14英寸 锐龙版R5', 4);
INSERT INTO `order` VALUES (5, '红辣椒7X 4+64GB 学生智能手机', 5);
INSERT INTO `order` VALUES (6, '荣耀10青春版 幻彩渐变', 1);
INSERT INTO `order` VALUES (7, 'OPPO K1 全面屏手机', 2);
INSERT INTO `order` VALUES (8, '卡梵蒂GAVADI 鳄鱼皮钱包', 5);
INSERT INTO `order` VALUES (9, '七匹狼钱包', 2);
INSERT INTO `order` VALUES (10, '金利来(Goldlion)男士钱包', 1);
2:Order
public class Order implements Serializable {
private int oid;
private String oname;
private int pid;
}
3:数据模型
Person{
List<Order> orderList;
}
4: 配置文件
<collection property="ods" column="id" ofType="Orders" select="调用其他mapper配置文件的查询 ">
collection>
示例代码 orderMapper
public interface OrderMapper {
List<Order> list();
}
<mapper namespace="beike.mybatis.mapper.OrderMapper">
<resultMap type="Order" id="OrderResultMap">
<id property="oid" column="oid">id>
<result property="oname" column="oname">result>
<result property="pid" column="pid">result>
resultMap>
<select id="list" resultMap="OrderResultMap">
SELECT * FROM `order` WHERE pid=#{pid}
select>
mapper>
PersonMapper
List<Person> getPersonOrder();
<mapper namespace="beike.mybatis.mapper.PersonMapper">
<resultMap type="Person" id="PersonOrderResultMap">
<id property="id" column="id">id>
<result property="name" column="name">result>
<result property="bir" column="bir"/>
<association property="car" column="id" javaType="Car" select="beike.mybatis.mapper.CarMapper.getCar" />
<collection property="orderList" column="id" ofType="Order" select="beike.mybatis.mapper.OrderMapper.list" />
resultMap>
<select id="getPersonOrder" resultMap="PersonOrderResultMap">
SELECT * FROM person
select>
mapper>
测试类
@Test
public void testGetPersonCar() {
//定义配置文件路径
String fileName="mybatis-config.xml";
try {
//通过配置文件创建输入流对象
InputStream inputStream = Resources.getResourceAsStream(fileName);
//读取配置文件 实例化我们工厂
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//通过反射机制获取接口对象
PersonMapper mapper = session.getMapper(PersonMapper.class);
//调用接口方法
List<Person> list = mapper.getPersonOrder();
//关闭
session.close();
System.out.println(list);
} catch (IOException e) {
e.printStackTrace();
}
}
多对多的模型设计
学生与课程:一个学生对应多门课程;一门课程对应多个学生
1: 表设计: Student Course stu_cou(中间关系表)
一个student对应多个stu_cou,一个stu_cou对应一个course
建表代码
#学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(20) DEFAULT NULL,
PRIMARY KEY (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `student` VALUES (1, '梦琪');
INSERT INTO `student` VALUES (2, '初夏');
INSERT INTO `student` VALUES (3, '忆柳');
#课程表
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`cid` int(11) NOT NULL AUTO_INCREMENT,
`cname` varchar(20) DEFAULT NULL,
PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `course` VALUES (1, 'JAVA');
INSERT INTO `course` VALUES (2, 'HTML');
INSERT INTO `course` VALUES (3, 'DATABASE');
#中间表
DROP TABLE IF EXISTS `stu_cou`;
CREATE TABLE `stu_cou` (
`scid` int(11) NOT NULL AUTO_INCREMENT,
`cid` int(11) DEFAULT NULL,
`sid` int(11) DEFAULT NULL,
PRIMARY KEY (`scid`),
KEY `scs` (`sid`),
KEY `scc` (`cid`),
CONSTRAINT `scc` FOREIGN KEY (`cid`) REFERENCES `course` (`cid`),
CONSTRAINT `scs` FOREIGN KEY (`sid`) REFERENCES `student` (`sid`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
INSERT INTO `stu_cou` VALUES (1, 1, 1);
INSERT INTO `stu_cou` VALUES (2, 1, 2);
INSERT INTO `stu_cou` VALUES (3, 1, 3);
INSERT INTO `stu_cou` VALUES (4, 2, 1);
INSERT INTO `stu_cou` VALUES (5, 2, 2);
INSERT INTO `stu_cou` VALUES (6, 2, 3);
INSERT INTO `stu_cou` VALUES (7, 3, 1);
2: 建立模型
/**
* 学生对课程中间表实体
*/
public class StuCou {
private int scid;
private int sid;
private int cid;
//一个cid对应一个课程对象
private Course course;
}
public class Student {
private int sid;
private String sname;
//一个学生对应多个中间对象 中间表中一个sid对应多个cid
private List<StuCou> scList;
}
public class Course {
private int cid;
private String cname;
}
3: 书写配置文件(通过查询学生关联查询学校)
public interface StudentMapper {
List<Student> list();
}
<mapper namespace="beike.mybatis.mapper.StudentMapper">
<resultMap type="Student" id="StudentCourseResultMap">
<id property="sid" column="sid">id>
<result property="sname" column="sname">result>
<!—一个student对应多个StuCou-->
<collection property="scList" column="sid" ofType="StuCou">
<id property="scid" column="scid">id>
<id property="cid" column="cid">id>
<id property="sid" column="sid">id>
<!—一个StuCou对应一个Course-->
<association property="course" column="cid" javaType="Course">
<id property="cid" column="cid">id>
<result property="cname" column="cname">result>
association>
collection>
resultMap>
<select id="listStudentCourse" resultMap="StudentCourseResultMap">
SELECT *
FROM student,stu_cou,course
WHERE student.sid=stu_cou.sid
AND stu_cou.cid=course.cid
select>
mapper>
测试类
@Test
public void testListStudentCourse() {
//定义配置文件路径
String fileName="mybatis-config.xml";
try {
//通过配置文件创建输入流对象
InputStream inputStream = Resources.getResourceAsStream(fileName);
//读取配置文件 实例化我们工厂
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
//通过反射机制获取接口对象
StudentMapper mapper = session.getMapper(StudentMapper.class);
//调用接口方法
List<Student> list = mapper.listStudentCourse();
//关闭
session.close();
for (Student student : list) {
System.out.println(student);
}
} catch (IOException e) {
e.printStackTrace();
}
}
一个方法适应多种操作
Public List<Obje ct> getInfo(StudentVo vo){
String sql=”select * from emp ”;
If(vo.getName()!=null){
Sql+=” where name=”+vo.getName();
}
If(vo.getAge()!=null){
Sql+=”and age=”+vo.getAge()
}
}
代替了我们在sql中的where关键字,自动忽略紧邻where标签中的and或者是or条件连接符
<select id="getAllPerson" resultMap="newPerson" parameterType="PersonVo">
select * from person
<where>
and name=#{names}
</where>
</select>
根据传递过来的参数进行判断,可以灵活的拼接sql语句
1: 传入的参数为自己定义的对象类型
<select id="getAllPerson" resultMap="newPerson" parameterType="PersonVo">
select * from person
<where>
判断条件中我们使用变量名是传入参数的属性名称
<if test="names != null">
and name=#{names}
</if>
<if test="bir != null ">
or bir =#{bir}
</if>
</where>
</select>
2: 常见的数据类型为查询参数 需要在mapper接口中定义方法的时候使用 @Param 指定参数的名称
<select id="getAllPerson" resultMap="newPerson">
select * from person
<where>
<if test="names != null">
and name=#{names}
</if>
</where>
</select>
注意:
1: 判断条件中可以使用当前数据类型的方法
2: 可以在动态sql中使用导航
<select id="getAllPerson" resultMap="newPerson" parameterType="PersonVo">
select * from person
<where>
<if test="dog.dogname.length()>1">
and name=#{dog.dogname}
</if>
</where>
</select>
3: 在判断的条件中可以使用and or 等逻辑运算符
<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
选择的动态sql,类似于jstl标签库中选择标签,类似于java 中switch case语句
<select id="getAllPerson" resultMap="newPerson">
select * from person
<where>
<choose>
<when test="names != null">
name=#{names}
</when>
<when test="bir != null">
bir = #{bir}
</when>
<otherwise>
id=1
</otherwise>
</choose>
</where>
</select>
注意:
1:Otherwise 标签时不能写到when标签前面
2: 我们只能从多个条件选取一个执行。
set标签就是代替sql语句中的set关键字,忽略最后成立条件的分隔符逗号
Update tname set name=ss,age=23,bir=ss where ….
<update id="updateInfo" parameterType="Person">
update person
<set>
<if test="age!=null">
age=#{age},
</if>
<if test="names !=null">
name = #{names},
</if>
</set>
where id=1
</update>
String trim() 去除字符串的前后空格
应用在where条件字句中,如果trim中包含的内容有成立的name就会在原有的sql基础上添加 prefix属性标记的前缀,如果没有成立,忽略trim标签,prefixOverrides=“and|or”,如果有条件成立,为了拼接sql语句会出现一些and or关键字 属性可以忽略第一个成立条件前面的and或者是or
<select id="getAllPerson" resultMap="newPerson" parameterType="PersonVo">
select * from person
<trim prefix="where" prefixOverrides="and|or" >
<if test="age != null">
age =#{age}
</if>
<if test="names !=null">
and name = #{names}
</if>
<if test="bir != null">
or bir = #{bir}
</if>
</trim>
</select>
条件成立在当前sql后面添加一个set关键字,如果后续的更新数据的字段后有逗号,trim会自动忽略我们的最后一个成立的逗号。
<update id="updateInfo" parameterType="Person">
update person
<trim prefix="set" suffixOverrides=",">
<if test="age!=null">
age=#{age},
</if>
<if test="names !=null">
name = #{names},
</if>
</trim>
where id=1
</update>
Foreach标签可以遍历传递的参数为集合类型的方法,批量的添加数据,批量更新数据
<insert id="batchAdd" parameterType="java.util.List">
insert into person(name,bir,age) values
<foreach collection="list" item="pp" separator=",">
(#{pp.names},#{pp.bir},#{pp.age})
</foreach>
</insert>
<delete id="batchDelete" parameterType="PersonVo">
delete from person where id in
<foreach collection="list" item="nid" open="(" close=")" separator=",">
#{nid}
</foreach>
</delete>
Foreach:
Collection=集合,直接list(关键字,不能写其它的)(传入的 参数就是我们的List),一个对象中的集合变量名称
Item:每次循环迭代的那一项数据
Index: 每次循环迭代的索引 从0开始递增
Separator: 每一次迭代的项和下一项之间的分隔符
Open: 整个循环的结果包含在哪种符号里,符号的开始
Close:整个循环的结果包含在哪种符号里,符号的结束
批量更新
<update id="batchupdate" parameterType="java.util.List">
<foreach collection="list" item="pa" separator=";">
update person
<set>
<if test="pa.age != null">
age = #{pa.age},
</if>
<if test="pa.names !=null">
name = #{pa.names},
</if>
<if test="pa.bir != null">
bir = #{pa.bir}
</if>
</set>
<where>
id = #{pa.id}
</where>
</foreach>
</update>
Mysql数据库自身是不允许批量更细的,所以我们要在数据库的连接的url中设定批量更新的参数
jdbc:mysql://localhost:3306/0903dbs?allowMultiQueries=true
resultMap 中的 association 和 collection 标签具有延迟加载的功能。
延迟加载的意思是说,在关联查询时,利用延迟加载,先加载主信息。需要关联信息
时再去按需加载关联信息。这样会大大 高数据库性能,因为查询单表要比关联查询多张
表速度要快。
关联对象数据:查询当前的人员信息,这一时刻不想去使用当前人对应的订单,在后面的逻辑会使用当前用户的关联数据。(按需加载)
Mybatis 默认是不开启延迟加载功能的,我们需要手动开启。
需要在 SqlMapConfig.xml 文件中,在 标签中开启延迟加载功能。
Mybatis设定了懒加载的设置模式:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="lazyLoadTriggerMethods" value=""/>
settings>
缓存:临时存储数据的空间,我们二次获取相同数据的时候我们就可以直接从临时存储空间获取,不用去数据库查询,减少数据库的io访问,提高程序的响应效率。
SqlSession: 就是我们一级缓存,必须使用的缓存机制,但是如果我们将session关闭之后一级缓存就失效了。
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息, 如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存, 这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息, 缓存中有,直接从缓存中获取用户信息。
Mybatis 默认支持一级缓存。
示例:
Person onePerson = mapper.getOnePerson(16);
System.out.println(onePerson);
//从缓存中获取的数据
Person onePerson2 = mapper.getOnePerson(16);
System.out.println(onePerson2);
二级缓存是 mapper 级别的。
1、第一次调用 mapper 下的 SQL 去查询用户信息。查询到的信息会存到该 mapper 对应的 二级缓存区域内。
2、第二次调用相同 namespace 下的 mapper 映射文件中相同的 SQL 去查询用户信息。会 去对应的二级缓存内取结果。
3、如果调用相同 namespace 下的 mapper 映射文件中的增删改 SQL,并执行了 commit 操作。 此时会清空该 namespace 下的二级缓存。
***\*开启二级缓存\****
Mybatis 默认是开启二级缓存 我们使用的版本默认是开启的
1、在核心配置文件 SqlMapConfig.xml 中加入以下内容(开启二级缓存总开关): 在 settings 标签中添加以下内容:
2、 在mapper配置文件中:
***\*实现序列化\****
由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给
缓存的对象执行序列化。
如果该类存在父类,那么父类也要实现序列化。
@Test
public void testTwoLevelCache() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询 ID 为 1 的用户,去缓存找,找不到就去查找数据库
User user1 = mapper1.findUserById(1);
System.out.println(user1);
// 关闭SqlSession1
sqlSession1.close();
// 第二次查询ID为1的用户
User user2 = mapper2.findUserById(1);
System.out.println(user2);
// 关闭SqlSession2
sqlSession2.close();
}
第一次缓存中没有记录,则命中率 0.0;
第二次缓存中有记录,则命中率 0.5(访问两次,有一次命中)
测试2
@Test
public void testTwoLevelCache() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
// 第一次查询 ID 为 1 的用户,去缓存找,找不到就去查找数据库
User user1 = mapper1.findUserById(1);
System.out.println(user1);
// 关闭 SqlSession1
sqlSession1.close();
// 修改查询出来的 user1 对象,作为插入语句的参数
user1.setUsername(" 东哥 1");
user1.setAddress(" 清河宝盛西里 ");
mapper3.insertUser(user1);
// 交事务
sqlSession3.commit();
// 关闭 SqlSession3
sqlSession3.close();
// 第二次查询ID为1的用户
User user2 = mapper2.findUserById(1);
System.out.println(user2);
// 关闭 SqlSession2
sqlSession2.close();
}
SQL 输出结果:
根据 SQL 分析,确实是清空了二级缓存了。
#{}: 占位符 PreparedStatement sql较为简洁,避免sql注入风险
${}: 字符串拼接 Statement,拼接字符串sql语句较为繁琐,sql注入风险。
<select id="findUsersByName" parameterType="String" resultType="com. offcn.po.User">
SELECT * FROM USER WHERE username LIKE '%${value}%'
select>
构建表:构建表对应数据模型,mapper接口,mapper.xml文件都需要我们手动完成,开发效率比较低,mybatis提供一个插件,可以帮助我们在开发中生成需要文件。
MybatisPlus---》mybatis框架的一个扩展工具,逆向工程,bean mapper service controller(了解)
1: 导入工具包
Generator-core.jar 逆向工程的核心jar包
2: 按照官方文档去创建一个配置文件
逆向工程的官网
http://www.mybatis.org/generator/running/runningWithJava.html:
3: 书写核心代码生成我们需要的文件。