目录
1.resultMap元素自定义结果集
1.1 要解决的问题:属性名和字段名不一致
1.1.1 数据库中的字段
1.1.2 Java中的实体类设计
package entity;
public class Banana {
private int bd;
private String bn;
private String bc;
private Place p;
private Btype bt;
public Banana(int bd, String bn, String bc, Place p, Btype bt) {
this.bd = bd;
this.bn = bn;
this.bc = bc;
this.p = p;
this.bt = bt;
}
//get,set,构造
}
1.1.3 接口
package dao;
import entity.Banana;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface BananaMapper {
/*查询所有香蕉*/
List<Banana>getall();
/*双表查询*/
List<Banana>getdoubleall();
/*查询海南产地下的所有香蕉*/
List<Banana>getPlace(String pname);
/*动态Mybatissql使用案例 if where*/
/*按编号查,按名字查,按颜色查*/
List<Banana>getfh(@Param("bid") String bid, @Param("bname") String bname, @Param("bcolor") String bcolor);
/*动态Mybatissql使用案例 foreach*/
/*按多颜色in查询*/
List<Banana>getBycolor(@Param("colors")String[] colors);
/*动态Mybatissql使用案例 set if*/
/*多参数修改*/
int update(Banana b);
}
1.1.4 mapper映射文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace=绑定一个对应的Dao/mapper接口-->
<mapper namespace="dao.BananaMapper">
<resultMap id="BananaMapall" type="entity.Banana">
<id column="bid" property="bd"/>
<result column="bname" property="bn"></result>
<result column="bcolor" property="bc"></result>
</resultMap>
<select id="getall" resultMap="BananaMapall">
select * from banana
</select>
<resultMap id="BananaMapfh" type="entity.Banana">
<id column="bid" property="bd"/>
<result column="bname" property="bn"></result>
<result column="bcolor" property="bc"></result>
</resultMap>
<select id="getfh" resultMap="BananaMapfh">
select * from banana
<where>
<if test="bid!=null and bid!=''">
bid=#{bid}
</if>
<if test="bname!=null and bname!=''">
and bname=#{bname}
</if>
<if test="bcolor!=null and bcolor!=''">
and bcolor=#{bcolor}
</if>
</where>
</select>
<select id="getBycolor" resultMap="BananaMapfh">
select * from banana
where bcolor
in
<foreach collection="colors" item="p" open="(" separator="," close=")" >
#{p}
</foreach>
</select>
<update id="update">
update banana
<set>
<if test="bn!=null and bn!=''">
bname=#{bn}
</if>
<if test="bc!=null and bc!=''">
, bcolor=#{bc}
</if>
</set>
where bid=#{bd}
</update>
<!--如果需要使用结果集映射需要先定义对应的结果集-->
<!--mybatis默认的按照同名进行表字段映射 双表查询必须要用resultMap-->
<!--定义的结果集映射-->
<resultMap id="BananaMap" type="entity.Banana">
<!--column数据库中的字段,property实体类中的属性-->
<!--主键列用id标识-->
<!--property:实体类字段名 column:表字段名-->
<!--association 用于实体类中的对象的映射-->
<id column="bid" property="bd"/>
<result column="bname" property="bn"></result>
<result column="bcolor" property="bc"></result>
<association property="p" javaType="entity.Place">
<id column="pid" property="pid"/>
<result column="pname" property="pname"></result>
</association>
<association property="bt" javaType="entity.Btype">
<id column="tid" property="tid"/>
<result column="tname" property="tname"></result>
</association>
</resultMap>
<select id="getdoubleall" resultMap="BananaMap">
select * from banana b
inner join place p on (b.pid=p.pid)
inner join btype bt on (b.tid=bt.tid)
</select>
<select id="getPlace" resultMap="BananaMap">
select * from banana b
inner join place p on (b.pid=p.pid)
inner join btype bt on (b.tid=bt.tid)
where p.pname=#{pname}
</select>
</mapper>
1.1.5 总结
当实体类中的字段名称与数据库中的字段名不一致,或者实体类中存在对象时需要开启resultMap,手动映射字段,不然找不到对应的字段数据为空!
偷懒小技巧:可在mybatis-config.xml核心配置文件settings标签中设置 <setting name="autoMappingBehavior" value="FULL"/>开启自动映射,当实体类存在对象时需要手动配置resultMap
<settings>
<!--严格区分大小写,空格-->
<!--STDOUT_LOGGING标准的日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--autoMappingBehavior开启resultMap自动装配 普通列自动映射,对象列不自动映射-->
<setting name="autoMappingBehavior" value="FULL"/>
<!--开启二级缓存(Mapper)-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.一对多与多对一处理
2.1 数据库设计
2.2 多对一的处理
多对一的理解:
- 多个学生对应一个老师
- 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!
2.2.1 实体类编写
@Data //GET,SET,ToString,有参,无参构造
public class Teacher {
private int id;
private String name;
}
@Data
public class Student {
private int id;
private String name;
//多个学生可以是同一个老师,即多对一
private Teacher teacher;
}
2.2.2 方式一:按查询嵌套处理
- 给StudentMapper接口增加方法
//获取所有学生及对应老师的信息 public List<Student> getStudents();
- 编写对应的Mapper文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.StudentMapper"> <!-- 需求:获取所有学生及对应老师的信息 思路: 1. 获取所有学生的信息 2. 根据获取的学生信息的老师ID->获取该老师的信息 3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般 使用关联查询? 1. 做一个结果集映射:StudentTeacher 2. StudentTeacher结果集的类型为 Student 3. 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,...)学生关联一个老师=> 一对一,一对多 4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查 询 --> <select id="getStudents" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多 的一方的表中的列名--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的 字段名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} </select> </mapper>
- 编写完毕去Mybatis配置文件中,注册Mapper
- 注意点说明
<resultMap id="StudentTeacher" type="Student"> <!--association关联属性 property属性名 javaType属性类型 column在多的一方 的表中的列名--> <association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/> </resultMap> <!-- 这里传递过来的id,只有一个属性的时候,下面可以写任何值 association中column多参数配置: column="{key=value,key=value}" 其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段 名。 --> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} and name = #{name} </select>
2.2.3 方式二:按结果嵌套处理
- 接口方法编写
public List<Student> getStudents2();
- 编写对应的mapper文件
<!-- 按查询结果嵌套处理 思路: 1. 直接查询出结果,进行结果集的映射 --> <select id="getStudents2" resultMap="StudentTeacher2" > select s.id sid, s.name sname , t.name tname from student s,teacher t where s.tid = t.id </select> <resultMap id="StudentTeacher2" type="Student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <!--关联对象property 关联对象在Student实体类中的属性--> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
- 去mybatis-config文件中注入【此处应该处理过了】
- 测试
@Test public void testGetStudents2(){ SqlSession session = MybatisUtils.getSession(); StudentMapper mapper = session.getMapper(StudentMapper.class); List<Student> students = mapper.getStudents2(); for (Student student : students){ System.out.println( "学生名:"+ student.getName() +"\t老师:"+student.getTeacher().getName()); } }
2.2.4 小结
- 按照查询进行嵌套处理就像SQL中的子查询
- 按照结果进行嵌套处理就像SQL中的联表查询
2.3 一对多的处理
一对多的理解:
- 一个老师拥有多个学生
- 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
2.3.1 实体类编写
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
//一个老师多个学生
private List<Student> students;
}
2.3.2 方式一:按结果嵌套处理
- TeacherMapper接口编写方法
//获取指定老师,及老师下的所有学生 public Teacher getTeacher(int id);
- 编写接口对应的Mapper配置文件
<mapper namespace="com.kuang.mapper.TeacherMapper"> <!-- 思路: 1. 从学生表和老师表中查出学生id,学生姓名,老师姓名 2. 对查询出来的操作做结果集映射 1. 集合的话,使用collection! JavaType和ofType都是用来指定对象类型的 JavaType是用来指定pojo中属性的类型 ofType指定的是映射到list集合属性中pojo的类型。 --> <select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname , t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id=#{id} </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="name" column="tname"/> <collection property="students" ofType="Student"> <result property="id" column="sid" /> <result property="name" column="sname" /> <result property="tid" column="tid" /> </collection> </resultMap> </mapper>
- 将Mapper文件注册到MyBatis-config文件中
<mappers> <mapper resource="mapper/TeacherMapper.xml"/> </mappers>
- 测试
@Test public void testGetTeacher(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }
2.3.3 方式二:按查询嵌套处理
- TeacherMapper接口编写方法
public Teacher getTeacher2(int id);
- 编写接口对应的Mapper配置文件
<select id="getTeacher2" resultMap="TeacherStudent2"> select * from teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="Teacher"> <!--column是一对多的外键 , 写的是一的主键的列名--> <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/> </resultMap> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{id} </select>
- 将Mapper文件注册到MyBatis-config文件中
- 测试
@Test public void testGetTeacher2(){ SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher2(1); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }
2.3.4 小结
- 关联-association
- 集合-collection
- 所以association是用于一对一和多对一,而collection是用于一对多的关系
- JavaType和ofType都是用来指定对象类型的
- JavaType是用来指定pojo中属性的类型
- ofType指定的是映射到list集合属性中pojo的类型。
注意说明:
- 保证SQL的可读性,尽量通俗易懂
- 根据实际要求,尽量编写性能更高的SQL语句
- 注意属性名和字段不一致的问题
- 注意一对多和多对一 中:字段和属性对应的问题
- 尽量使用Log4j,通过日志来查看自己的错误
3.Mybatis注解增删改查
3.1 查询
//根据id查询用户
@Select("select * from user where id = #{id}")
User selectUserById(@Param("id") int id);
3.2 增加
//添加一个用户
@Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
int addUser(User user);
3.3 修改
//修改一个用户
@Update("update user set name=#{name},pwd=#{pwd} where id = #{id}")
int updateUser(User user);
3.4 删除
//根据id删除用户
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id")int id);
【注意点:增删改一定记得对事务的处理】
3.5 小结
对于简单的sql语句推荐使用注解,更加方便快捷,如果是比较复杂的sql语句推荐使用映射文件。
3.6 关于@Param注解
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
- 在方法只接受一个参数的情况下,可以不使用@Param。
- 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
- 如果参数是 JavaBean , 则不能使用@Param。
- 不使用@Param注解时,参数只能有一个,并且是Javabean。
3.7 #{}与${}的区别
- #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
- ${} 的作用是直接进行字符串替换
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('fangge');
4.Mybatis详细的执行流程
5.Mybatis框架的缓存
5.1 缓存简介
- 什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
5.2 Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
5.3 一级缓存
一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
5.4 一级缓存失效的四种情况
- 一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
- 一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
- sqlSession不同
- sqlSession相同,查询条件不同
- sqlSession相同,两次查询之间执行了增删改操作!
- sqlSession相同,手动清除一级缓存
5.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
5.5 使用步骤
官方文档:mybatis – MyBatis 3 | XML 映射器
- 开启全局缓存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
- 去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】
<cache/> 官方示例=====>查看官方文档 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者 产生冲突。
- 代码测试
- 所有的实体类先实现序列化接口
- 测试代码
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.close(); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session2.close(); }
5.6 结论
- 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
- 查出的数据都会被默认先放在一级缓存中
- 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
6.缓存原理
7.本章总结
- Mybatis框架的SQL映射文件提供select丶insert丶update丶delete等元素来实现SQL语句的映射。
- SQL映射文件的根节点是mapper元素,其namespace属性的值需要保证全局唯一,用于区分不同的mapper。
- 基于面向接口编程的理念,mapper元素的namespace属性值应指定为Mapper接口的完全限定类名。
- SQL映射文件select元素可以使用resultMap或resultType指定返回结果的类型,但是二者不能同时使用。
- 为Mapper接口方法传入多个简单类型的参数时,建议使用@Param注解为参数命名。
- 在resultMap元素中可以使用association元素和collection元素实现嵌套结果映射。