Mybatis
-
Mybatis简介
MyBatis 是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
搭建第一个Mybatis工程
- 第一步:创建Java Maven项目
- 第二步:导入pom.xml坐标
- 第三步:创建Mybatis的核心配置文件
- 第四步:创建实体类和数据表
- 第五步:创建实体类的映射文件
-
Mybatis坐标
pom.xml
<dependencies> <!--mybatis的依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!--单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--mysql的驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <!--日志:记录应用程序所有的执行过程--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
-
mybatis-config.xml配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--数据库的配置信息,且和spring整合结束,这些配置由sping进行管理--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/databasis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--添加映射--> <mappers> <mapper resource="com/xszx/dao/UserMapper.xml"></mapper> </mappers> </configuration>
-
创建实体类和数据表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qxX8QMZ3-1628342290653)(C:\Users\LJW\AppData\Roaming\Typora\typora-user-images\1625728881488.png)]
-
实体类的映射文件
UserMapper.xml
<?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.xszx.dao.UserDao">
<!--方法名和参数类型-->
<insert id="add" parameterType="com.xszx.entity.User">
<!--sql语句-->
insert into user values (null,#{username},#{password});
</insert>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
<update id="updateUser" parameterType="com.xszx.entity.User">
update user set username=#{username},password=#{password} where id=#{id}
</update>
<select id="getUser" resultType="com.xszx.entity.User">
select * from user
</select>
<select id="getUserById" resultType="com.xszx.entity.User">
select * from user where id=#{id}
</select>
</mapper>
注意事项:
namespace命名空间必须为XXXDao的全限定类名
标签中的增删改查的id必须是XXXDao中的方法名称
paraneterType标明方法中的参数类型
书写sql语句过程中,需要#{参数}替换
7. 测试
@Test
public void add() throws IOException {
//读取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//创建核心SqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂类创建Sqlsession
SqlSession sqlSession=sqlSessionFactory.openSession();
//通过反射创建UserDao的子实现类
UserDao userDao=sqlSession.getMapper(UserDao.class);
User user=new User();
user.setUsername("admin");
user.setPassword("123456");
userDao.add(user);
//提交事务
sqlSession.commit();
}
参数处理
-
参数个数为基本类型或者字符串类型,而且个数为单个的时候,传参的过程中,#{}当中可以随意书写变量
-
参数类型为自定义对象,而且个数为单个的时候,传参过程中,#{}当中的名称必须是自定义对象中的属性(有相对应的get方法的属性)
-
参数个数为多个的时候,解决办法:
-
在#{}中写固定框架底层的参数名称
<select id="login" resultType="com.xszx.entity.User" parameterType="java.lang.String"> select * from user where username=#{param1} and password=#{param2} </select>
-
在XXXDao的形参钱追加@Param
User login(@Param("username") String username, @Param("password") String password);
-
在传参的过程中传递自定义对象类型
User login(User user);
-
在传参过程中,传递map类型,需注意在#{}中写map集合中的key值
User login(HashMap map); //UserMapper.xml中 select * from user where username=#{ username} and password=#{ password}
-
返回值处理
-
返回值为自定义对象
User getUserById(int id); <select id="getUserById" resultType="com.zx.entity.User"> select * from t_user where id = #{id} </select>
-
返回值为对象的一个属性
String getUsernameById(int id); <select id="getUsernameById" resultType="java.lang.String"> select username from t_user where id = #{id} </select>
-
返回值为对象中的某几个属性
-
对象接收
User getUsernameandAgeById1(int id); <select id="getUsernameandAgeById1" resultType="com.zx.entity.User"> select id,username,age from t_user where id = #{id} </select>
-
HashMap接受
HashMap getUsernameandAgeById(int id); <select id="getUsernameandAgeById" resultType="map"> select id,username,age from t_user where id = #{id} </select>
-
DTO模式接受
Temp getUsernameandAgeById2(int id); Temp getUsernameandGnameById3(int id); <select id="getUsernameandAgeById2" resultType="com.zx.entity.Temp"> select username,age from t_user where id = #{id} </select> <select id="getUsernameandGnameById3" resultType="com.zx.entity.Temp"> select u.username,g.gname from t_user u,t_group g where u.gid = g.gid and u.id = #{id} </select>
-
-
多对一关联映射
-
resultType 返回单个对象的属性
-
resultMap 返回关联对象的属性
-
属性 id 唯一标识
-
属性 type 最终返回类型
-
子标签:
-
id 主键
- column 表中的字段
- property 实体类中的属性
-
result 普通属性
- column 表中的字段
- property 实体类中的属性
-
association 关联映射标签
- 属性column 表中的字段(外键)
- 属性property 实体中的属性
- 属性select 查询被关联的一方 命名空间.id
- 属性 javaType 查询被关联的一方的类型
-
-
方式一:发送多次sql语句,每张表进行单独查询,通过select属性向对方映射文件中进行查询
association: 关联标签 出现在“多”方
column:数据表中的列名称
property:实体类中的属性
select: 需要指定命名空间.id去查询关联对象
javaType: 查询结束之后的返回类型
<select id="getUser04" parameterType="int" resultMap="userResult">
select * from t_user where id = #{id}
</select>
<resultMap id="userResult" type="com.zx.entity.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="age" property="age"></result>
<association column="gid" property="group" select="com.zx.dao.GroupDao.getGroupByid" javaType="com.zx.entity.Group"></association>
</resultMap>
方式二:发送一次sql语句,进行联表查询,需要注意SQL语句的性能,不需要使用select属性
association: 关联标签 出现在“多”方
column:数据表中的列名称
property:实体类中的属性
javaType: 查询结束之后的返回类型
association打开,可以嵌入子标签:
id:对方表中的主键
column:数据表中的列名称
property:实体类中的属性
result:其他属性
column:数据表中的列名称
property:实体类中的属性
<select id="getUser05" parameterType="int" resultMap="userResult05">
select u.id uid,u.username username,u.password password,u.age age,g.id gid,g.gname gname from t_user u,t_group g where u.gid = g.id and u.id = #{id};
</select>
<resultMap id="userResult05" type="com.zx.entity.User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="age" property="age"></result>
<association column="gid" property="group" javaType="com.zx.entity.Group">
<id column="gid" property="id"></id>
<result column="gname" property="gname"></result>
</association>
</resultMap>
一对多集合映射
方式一:发送多次sql语句,每张表进行单独查询,通过select属性向对方映射文件中进行查询
<select id="getGroupByid02" resultMap="groupResult02" parameterType="int">
select * from t_group where id = #{id}
</select>
<resultMap id="groupResult02" type="com.zx.entity.Group">
<id column="id" property="id"></id>
<result column="gname" property="gname"></result>
<collection column="id" property="users" select="com.zx.dao.UserDao.getUsersByGid" ofType="com.zx.entity.User" ></collection>
</resultMap>
方式二:发送一次sql语句,进行联表查询,需要注意SQL语句的性能,不需要使用select属性
collection打开,可以嵌入子标签:
id:对方表中的主键
column:数据表中的列名称
property:实体类中的属性
result:其他属性
column:数据表中的列名称
property:实体类中的属性
<select id="getGroupByid03" resultMap="groupResult03" parameterType="int">
select g.id gid,g.gname gname,u.id uid,u.username username,u.password password,u.age age from t_user u,t_group g where u.gid = g.id and g.id = #{gid}
</select>
<resultMap id="groupResult03" type="com.zx.entity.Group">
<id column="gid" property="id"></id>
<result column="gname" property="gname"></result>
<collection column="gid" property="users" ofType="com.zx.entity.User" >
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="age" property="age"></result>
</collection>
</resultMap>
多对多集合映射
<select id="getStudentBySid" parameterType="int" resultMap="studentResult01">
select * from t_student where sid = #{sid}
</select>
<resultMap id="studentResult01" type="com.zx.entity.Student">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
<collection column="sid" property="teachers" select="com.zx.dao.TeacherDao.getTeacherByTid" ofType="com.zx.entity.Teacher"></collection>
</resultMap>
<select id="getTeacherBySid" parameterType="int" resultType="com.zx.entity.Teacher">
select * from t_teacher where tid in (select tid from t_student_teacher where sid = #{sid})
</select>
<select id="getTeacherByTid" parameterType="int" resultMap="teacherResult">
select t.tid tid,t.tname tname,s.sid sid,s.sname sname from
t_teacher t left join t_student_teacher st on t.tid = st.tid
left join t_student s on st.sid = s.sid
where t.tid = #{tid}
</select>
<resultMap id="teacherResult" type="com.zx.entity.Teacher">
<id column="tid" property="tid"></id>
<result column="tname" property="tname"></result>
<collection column="tid" property="students" ofType="com.zx.entity.Student">
<id column="sid" property="sid"></id>
<result column="sname" property="sname"></result>
</collection>
</resultMap>
缓存机制
一级缓存 SqlSession缓存 (默认开启)
二级缓存 SqlSessionFactory缓存
手动开启二级缓存
- 第一步:在映射文件中追加二级缓存配置
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
-
第二步:在mybatis中的配置文件中开启二级缓存开关
<settings> <!--这个配置使全局的映射器(二级缓存)启用或禁用缓存--> <setting name="cacheEnabled" value="true" /> ..... </settings> 注意事项:在新版本中默认不需要在这里开启
-
第三步:如果某些方法不想使用二级缓存,在方法中的select标签中可以设置关闭二级缓存
<select id="getUsers" resultType="com.zx.entity.User" useCache="false">
抓取策略
fetchType
lazy 懒加载
eager 及时加载
从一方查询多方的时候,需要考虑多方的数据量
如果只是使用一方的数据,而不使用多方数据的情况下,在执行过程中不需要发送查询多方的sql语句,需要配置抓取策略为 懒加载
在配置好懒加载的前提下,使用一方数据结束,马上关闭sqlSession之后,想要使用多方数据,在mybatis中可以正常执行
在未来的Hibernate或者SpringDataJPA中,执行上述步骤要报错 no session