01.MyBatis环境搭建
-
添加MyBatis的坐标
<!--mybatis坐标--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!--mysql驱动坐标--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <scope>runtime</scope> </dependency> <!--单元测试坐标--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> </dependency>
-
创建数据表
-
编写DO实体类
-
编写映射⽂件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.muchfish.dao.IUserDao"> <!--namespace : 名称空间:与id组成sql的唯一标识 resultType: 表明返回值类型--> <!--查询用户--> <select id="findAll" resultType="com.muchfish.pojo.User"> select * from User </select> </mapper>
-
编写核⼼⽂件SqlMapConfig.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> <!--加载外部的properties文件--> <properties resource="jdbc.properties"/> <!--environments:运行环境--> <environments default="development"> <environment id="development"> <!--当前事务交由JDBC进行管理--> <transactionManager type="JDBC"/> <!--当前使用mybatis提供的连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--引入映射配置文件--> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql:///mybatis jdbc.username=root jdbc.password=123456
-
编写测试类
@Test public void test1() throws IOException { //1.Resources工具类,配置文件的加载,把配置文件加载成字节输入流 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //2.解析了配置文件,并创建了sqlSessionFactory工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); //3.生产sqlSession SqlSession sqlSession = sqlSessionFactory.openSession();// 默认开启一个事务,但是该事务不会自动提交 //在进行增删改操作时,要手动提交事务 //4.sqlSession调用方法:查询所有selectList 查询单个:selectOne 添加:insert 修改:update 删除:delete List<User> users = sqlSession.selectList("com.muchfish.dao.IUserDao.findAll"); for (User user : users) { System.out.println(user); } sqlSession.close(); }
02.MyBatis的CRUD
-
CRUD的API
- sqlSession.selectList()、sqlSession.selectOne()
- sqlSession.insert()
- sqlSession.update()
- sqlSession.delete()
-
注意问题
-
在进行增删改操作时,要手动提交事务。
sqlSessionFactory.openSession()默认开启一个事务,但是该事务不会自动提交
-
mapper.xml中的Sql语句中使⽤#{任意字符串}⽅式引⽤传递的单个参数
<!--删除--> <delete id="deleteUser" parameterType="int"> delete from user where id = #{abc} </delete>
-
sqlSession.close():释放资源
-
sqlSession.commit()
-
sqlSession.rollback()
-
sqlSessionFactory.openSession(true):事务自动提交
-
03.MyBatis相关配置文件
sqlMapConfig.xml
-
mapper标签
该标签的作⽤是加载映射的,加载⽅式有如下⼏种:
•使⽤相对于类路径的资源引⽤,例如: <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> •使⽤完全限定资源定位符(URL),例如: <mapper url="file:///var/mappers/AuthorMapper.xml"/> •使⽤映射器接⼝实现类的完全限定类名,例如: <mapper class="org.mybatis.builder.AuthorMapper"/> 注意:保证接口名和xml文件名一致且包结构一致(是否包结构一直皆可,文件名可以不一致) •将包内的映射器接⼝实现全部注册为映射器,例如: <package name="org.mybatis.builder"/> 注意:保证接口名和xml文件名一致且包结构一致。(是否包结构一直皆可,文件名可以不一致。测试得:文件名也必须一致)
XXXmapper.xml
04.MyBatis的Dao层代理开发方式与mappers标签测试
mappers标签测试
•使⽤映射器接⼝实现类的完全限定类名,例如:
<mapper class="com.muchfish.dao.IUserDao"/>
注意:保证接口名和xml文件名一致且包结构一致
•将包内的映射器接⼝实现全部注册为映射器,例如:
<package name="com.muchfish.dao"/>
注意:保证接口名和xml文件名一致且包结构一致。
Dao层代理开发
Mapper 接⼝开发需要遵循以下规范:
- Mapper.xml⽂件中的namespace与mapper接⼝的全限定名相同
- Mapper接⼝⽅法名和Mapper.xml中定义的每个statement的id相同
- Mapper接⼝⽅法的输⼊参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接⼝⽅法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
05.MyBatis的多对多复杂映射
-
DO类
public class User { private Integer id; private String username; private String password; private String birthday; //表示用户关联的角色 private List<Role> roleList = new ArrayList<>(); //。。。省略getter/setter }
-
Mapper.xml
<resultMap id="userRoleMap" type="com.muchfish.pojo.User"> <result property="id" column="userid"></result> <result property="username" column="username"></result> <collection property="roleList" ofType="com.muchfish.pojo.Role"> <result property="id" column="roleid"></result> <result property="roleName" column="roleName"></result> <result property="roleDesc" column="roleDesc"></result> </collection> </resultMap> <select id="findAllUserAndRole" resultMap="userRoleMap"> select * from user u left join sys_user_role ur on u.id = ur.userid left join sys_role r on r.id = ur.roleid </select>
-
Dao接口
public interface IUserDao { public List<User> findAllUserAndRole(); }
06.MyBatis的注解开发
-
MyBatis的常⽤注解
- @Insert:实现新增
- @Update:实现更新
- @Delete:实现删除
- @Select:实现查询
- @Result:实现结果集封装
- @Results:可以与@Result ⼀起使⽤,封装多个结果集
- @One:实现⼀对⼀结果集封装
- @Many:实现⼀对多结果集封装
-
注解一对多查询
public interface IOrderDao { //查询订单的同时还查询该订单所属的用户 @Results({ @Result(property = "id",column = "id"), @Result(property = "orderTime",column = "orderTime"), @Result(property = "total",column = "total"), @Result(property = "user",column = "uid",javaType = User.class, one=@One(select = "com.muchfish.dao.IUserDao.findUserById")) }) @Select("select * from orders") public List<Order> findOrderAndUser(); @Select("select * from orders where uid = #{uid}") public List<Order> findOrderByUid(Integer uid); }
- 注解和xml混合使用命中同一个statementId会报错
07.MyBatis缓存
⼀级缓存
private IUserDao userMapper;
private SqlSession sqlSession;
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
userMapper = sqlSession.getMapper(IUserDao.class);
}
@Test
public void test1() {
//第⼀次查询,发出sql语句,并将查询出来的结果放进缓存中
User u1 = userMapper.findUserById(1);
System.out.println(u1);
//第⼆次查询,由于是同⼀个sqlSession,会在缓存中查询结果
//如果有,则直接从缓存中取出来,不和数据库进⾏交互
User u2 = userMapper.findUserById(1);
System.out.println(u2);
sqlSession.close();
}
查看控制台打印情况:
@Test
public void test2(){
//根据 sqlSessionFactory 产⽣ session
//第⼀次查询,发出sql语句,并将查询的结果放⼊缓存中
User u1 = userMapper.findUserById( 1 );
System.out.println(u1);
//第⼆步进⾏了⼀次更新操作, sqlSession.commit()
u1.setPassword("23131");
userMapper.updateUserByUserId(u1);
sqlSession.commit();
//第⼆次查询,由于是同⼀个sqlSession.commit(),会清空缓存信息
//则此次查询也会发出sql语句
User u2 = userMapper.findUserById(1);
System.out.println(u2);
sqlSession.close();
}
查看控制台打印情况:
日志略:第⼆次查询会打印sql语句
- 总结
- 第⼀次发起查询⽤户id为1的⽤户信息,先去找缓存中是否有id为1的⽤户信息,如果没有,从 数据库查询⽤户信息。得到⽤户信息,将⽤户信息存储到⼀级缓存中。
- 如果中间sqlSession去执⾏commit操作(执⾏插⼊、更新、删除),则会清空SqlSession中的 ⼀级缓存,这样做的⽬的为了让缓存中存储的是最新的信息,避免脏读。
- 第⼆次发起查询⽤户id为1的⽤户信息,先去找缓存中是否有id为1的⽤户信息,缓存中有,直 接从缓存中获取⽤户信息
二级缓存
-
使用二级缓存
-
开启⼆级缓存
-
在全局配置⽂件sqlMapConfig.xml⽂件中开启
<!--开启⼆级缓存 注意<settings>标签的顺序,在<properties>标签后面--> <settings> <setting name="cacheEnabled" value="true"/> </settings>
-
在Mapper.xml⽂件中开启缓存
<!--使用二级缓存--> <cache></cache>
-
在DAO接口中开启缓存(可选。使用@CacheNamespace会报错。)
@CacheNamespace public interface IUserDao { }
或
@CacheNamespaceRef(IUserDao.class) //@CacheNamespace public interface IUserDao { }
- xml和对应dao接口上同时开启二级缓存会报错,此时只能使用@CacheNamespaceRef
-
-
DO序列化
public class User implements Serializable { //。。。 }
-
测试
@Test public void SecondLevelCache(){ //根据 sqlSessionFactory 产⽣ session SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); String statement = "com.lagou.pojo.UserMapper.selectUserByUserld" ; IUserDao userMapper1 = sqlSession1.getMapper(IUserDao. class ); IUserDao userMapper2 = sqlSession2.getMapper(IUserDao. class ); IUserDao userMapper3 = sqlSession2.getMapper(IUserDao. class ); //第⼀次查询,发出sql语句,并将查询的结果放⼊缓存中 User u1 = userMapper1.findById( 1 ); System.out.println(u1); sqlSession1.close(); //第⼀次查询完后关闭sqlSession //执⾏更新操作, commit()。注释掉此处,sqlSession2的查询会走缓存。放开此处,会走数据库 // u1.setUsername( "aaa" ); // userMapper3.updateUserByUserId(u1); // sqlSession3.commit(); //第⼆次查询,由于上次更新操作,缓存数据已经清空(防⽌数据脏读),这⾥必须再次发出sql语 User u2 = userMapper2.findById( 1 ); System.out.println(u2); sqlSession2.close(); }
-
-
useCache和flushCache
-
useCache:开启或禁用缓存
<select id="findById" resultType="com.muchfish.pojo.User" useCache="true"> select * from user where id = #{id} </select>
-
flushCache:刷新缓存
<select id="findById
-