基础概念
对象/关系数据库映射(ORM)
ORM全称Object/Relation Mapping:表示对象-关系映射的缩写
ORM完成⾯向对象的编程语⾔到关系数据库的映射。当ORM框架完成映射后,程序员既可以利⽤⾯向对象程序设计语⾔的简单易⽤性,⼜可以利⽤关系数据库的技术优势。ORM把关系数据库包装成⾯向对
象的模型。ORM框架是⾯向对象设计语⾔与关系数据库发展不同步时的中间解决⽅案。采⽤ORM框架后,应⽤程序不再直接访问底层数据库,⽽是以⾯向对象的放松来操作持久化对象,⽽ORM框架则将这
些⾯向对象的操作转换成底层SQL操作。ORM框架实现的效果:把对持久化对象的保存、修改、删除等操作,转换为对数据库的操作
配置文件
层级关系
1)environments标签
其中,事务管理器(transactionManager)类型有两种:
•JDBC:这个配置就是直接使⽤了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作
⽤域。
•MANAGED:这个配置⼏乎没做什么。它从来不提交或回滚⼀个连接,⽽是让容器来管理事务的整个⽣
命周期(⽐如 JEE 应⽤服务器的上下⽂)。 默认情况下它会关闭连接,然⽽⼀些容器并不希望这样,因
此需要将 closeConnection 属性设置为 false 来阻⽌它默认的关闭⾏为。
其中,数据源(dataSource)类型有三种:
•UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
•POOLED:这种数据源的实现利⽤“池”的概念将 JDBC 连接对象组织起来。•JNDI:这个数据源的实现是为了能在如 EJB 或应⽤服务器这类容器中使⽤,容器可以集中或在外部配
置数据源,然后放置⼀个 JNDI 上下⽂的引⽤。
2)mapper标签
•使⽤相对于类路径的资源引⽤,例如: <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
•使⽤完全限定资源定位符(URL),例如: <mapper url="file:///var/mappers/AuthorMapper.xml"/>
•使⽤映射器接⼝实现类的完全限定类名,例如: <mapper class="org.mybatis.builder.AuthorMapper"/>
注意:此种方法要求 mapper 接口名称和 mapper 映射文件名称相同,且放在同一个目录中
•将包内的映射器接⼝实现全部注册为映射器,例如: <package name="org.mybatis.builder"/>
3)Properties标签
resource属性:用于指定 properties 配置文件的位置,要求配置文件必须在类路径下
resource="jdbcConfig.properties"
4)typeAliases标签
<!-- 单个别名定义 --> <typeAlias alias="user" type="com.lagou.pojo.User"/> <!-- 批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) --> <package name="com.lagou.domain"/>
5)动态sql
1.<if test=条件判断></if>
注意问题:1.条件判断中不能使用&&,要使用and 2.<if>标签的 test 属性中写的是对象的属性
2.<where></where>
简化 where 1=1 的条件拼装,我们可以采用<where>标签来简化开发,会自动删去第一个and
3.<foreach> </foreach>
<foreach>标签用于遍历集合 collection: 代表要遍历的集合元素,注意编写时不要写#{} open: 代表语句的开始部分 close: 代表结束部分 item: 代表遍历集合的每个元素,生成的变量名 separator: 代表分隔符
4.<select>
抽取重复的语句代码片段 include refid="defaultSql"></include> <sql id="defaultSql"> select * from user </sql>
复杂映射
一对一
实体中表示一方关系:实体
使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。
<association property="user" javaType="com.lagou.pojo.User">
<!-- 一对一映射 --> <resultMap id="orderMap" type="com.mytest.pojo.one_one.Order"> <result property="id" column="id"></result> <result property="orderTime" column="orderTime"></result> <result property="total" column="total"></result> <association property="user" javaType="com.mytest.pojo.one_one.User"> <result property="id" column="uid"></result> <result property="username" column="username"></result> </association> </resultMap> <!--resultMap:手动来配置实体属性与表字段的映射关系--> <select id="findOrderAndUser" resultMap="orderMap"> select * from orders o,user u where o.uid = u.id </select>
一对多
实体中表示多方关系:集合 使用 resultMap,定义专门的 resultMap 用于映射一对多查询结果。 <collection property="orderList" ofType="com.lagou.pojo.Order">
<!-- 一对多映射 --> <resultMap id="userMap" type="com.mytest.pojo.one_many.User"> <result property="id" column="uid"></result> <result property="username" column="username"></result> <collection property="orderList" ofType="com.mytest.pojo.one_many.Order"> <result property="id" column="id"></result> <result property="orderTime" column="orderTime"></result> <result property="total" column="total"></result> </collection> </resultMap> <select id="findAll" resultMap="userMap"> select * from user u left join orders o on u.id = o.uid </select>
多对多
双方实体中表示对方关系:集合 使用 resultMap,定义专门的 resultMap 用于映射一对多查询结果。 <collection property="orderList" ofType="com.lagou.pojo.Order">
<!-- 多对多映射 --> <resultMap id="userRoleMap" type="com.mytest.pojo.many_many.Users"> <result property="id" column="userid"></result> <result property="username" column="username"></result> <collection property="roleList" ofType="com.mytest.pojo.many_many.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>
public class MybatisTest { @Test public void testOne_one() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); List<Order> orderAndUser = mapper.findOrderAndUser(); for (Order order : orderAndUser) { System.out.println(order); } } @Test public void testOne_many() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); List<User> userList = mapper.findAll(); for (User user : userList) { System.out.println(user); } } @Test public void testMany_many() throws IOException { InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); List<Users> userList = mapper.findAllUserAndRole(); for (Users user : userList) { System.out.println(user); } } }
注解开发
public interface IUserMappers { /* 单注解增删改查 */ //添加用户 @Insert("insert into user values(#{id},#{username})") public void addUser(User user); //更新用户 @Update("update user set username = #{username} where id = #{id}") public void updateUser(User user); //查询用户 @Select("select * from user") public List<User> selectUser(); //删除用户 @Delete("delete from user where id = #{id}") public void deleteUser(Integer id); /*--------------------------------------------------------*/ /* 一对一注解 */ //根据id查询用户 @Select({"select * from user where id = #{id}"}) public User findUserById(Integer id); /* 一对多注解 */ //查询所有用户、同时查询每个用户关联的订单信息 @Select("select * from user") @Results({ @Result(property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "orderList",column = "id",javaType = List.class, many=@Many(select = "com.mytest.mapper.IOrderMapper.findOrderByUid")) }) public List<com.mytest.pojo.one_many.User> findAll(); /* 多对多注解 */ //查询所有用户、同时查询每个用户关联的角色信息 @Select("select * from user") @Results({ @Result(property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "roleList",column = "id",javaType = List.class, many = @Many(select = "com.mytest.mapper.IRoleMapper.findRoleByUid")) }) public List<com.mytest.pojo.many_many.Users> findAllUserAndRole(); }
public class MybatisTest1 { private IOrderMapper orderMapper; private IUserMappers userMappers; @Before public void befor() throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true); userMappers = sqlSession.getMapper(IUserMappers.class); orderMapper = sqlSession.getMapper(IOrderMapper.class); } /* 单注解的增删改查 */ @Test public void addUser(){ User user = new User(); user.setId(3); user.setUsername("测试数据"); userMappers.addUser(user); } @Test public void updateUser(){ User user = new User(); user.setId(3); user.setUsername("修改了测试数据"); userMappers.updateUser(user); } @Test public void selectUser(){ List<User> users = userMappers.selectUser(); for (User user : users) { System.out.println(user); } } @Test public void deleteUser(){ userMappers.deleteUser(3); } /* 复杂注解 */ @Test public void oneToOne(){ List<Order> orderAndUser = orderMapper.findOrderAndUser(); for (Order order : orderAndUser) { System.out.println(order); } } @Test public void oneToMany(){ List<com.mytest.pojo.one_many.User> all = userMappers.findAll(); for (com.mytest.pojo.one_many.User user : all) { System.out.println(user); } } @Test public void ManyToMany(){ List<com.mytest.pojo.many_many.Users> all = userMappers.findAllUserAndRole(); for (com.mytest.pojo.many_many.Users user : all) { System.out.println(user); } } }
public interface IRoleMapper { /* 多对多注解 */ @Select("select * from sys_role r,sys_user_role ur where r.id = ur.roleid and ur.userid = #{uid}") public List<Role> findRoleByUid(Integer uid); }
public interface IOrderMapper { /* 一对一注解 */ //查询订单的同时还查询该订单所属的用户 @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.mytest.mapper.IUserMappers.findUserById")) }) @Select("select * from orders") public List<Order> findOrderAndUser(); /* 一对多注解 */ @Select("select * from orders where uid = #{uid}") public List<Order> findOrderByUid(Integer uid); }
缓存原理
二级缓存与一级缓存区别,
二级缓存的范围更大,多个sqlSssion可以共享一个userMapper的二级缓存区域。userMapper有一个二级缓存区域(安namespance分)。其他mapper也由自己的二级缓存区域(安namespance分)
每个namespance的mapper都有一个二级缓存区域,两个mapper的namespance如果相同,这两个mapper执行sql查询到的数据将存在相同的二级缓存区域中
1.一级缓存
1.一级缓存默认开启
2.一级缓存运行过程
3.更新操作后查看一级缓存
总结:
1、第⼀次发起查询⽤户id为1的⽤户信息,先去找缓存中是否有id为1的⽤户信息,如果没有,从 数据
库查询⽤户信息。得到⽤户信息,将⽤户信息存储到⼀级缓存中。
2、 如果中间sqlSession去执⾏commit操作(执⾏插⼊、更新、删除),则会清空SqlSession中的 ⼀
级缓存,这样做的⽬的为了让缓存中存储的是最新的信息,避免脏读。
3、 第⼆次发起查询⽤户id为1的⽤户信息,先去找缓存中是否有id为1的⽤户信息,缓存中有,直 接从
缓存中获取⽤户信息
问题:
1.只进行 insert、updat、delelte 会不会清空缓存,为什么?
4.⼀级缓存原理探究与源码分析
1)⼀级缓存到底是什么?
2)⼀级缓存什么时候被创建?
3)⼀级缓存的⼯作流程是怎样的?
你觉得最有可能创建缓存的地⽅是哪⾥呢?我觉得是Executor,为什么这么认为?
因为Executor是 执⾏器,⽤来执⾏SQL请求,⽽且清除缓存的⽅法也在Executor中执⾏,所以很可能缓存的创建也很 有可
能在Executor中,看了⼀圈发现Executor中有⼀个createCacheKey⽅法,这个⽅法很像是创 建缓存的
⽅法啊,跟进去看看,你发现createCacheKey⽅法是由BaseExecutor执⾏的,代码如下
CacheKey cacheKey = new CacheKey(); 一级缓存就是个Map //MappedStatement 的 id // id就是Sql语句的所在位置包名+类名+ SQL名称 cacheKey.update(ms.getId()); // offset 就是 0 cacheKey.update(rowBounds.getOffset()); // limit 就是 Integer.MAXVALUE cacheKey.update(rowBounds.getLimit()); //具体的SQL语句 cacheKey.update(boundSql.getSql()); //后⾯是update 了 sql中带的参数 cacheKey.update(value); ... if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); }
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/>
<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>
2)⼀级缓存什么时候被创建?
执行器接口中有一个
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
3.执行流程
2.二级缓存
二级缓存需要手动开启
1.核心配置文件中添加setting
<!--开启⼆级缓存--> <settings> <setting name="cacheEnabled" value="true"/> </settings>
2.映射配置文件添加 cache (注意:如果是注解开发方式,在接口中添加 @CacheNamespace)
<!--开启⼆级缓存--> <cache></cache>
3.对象序列化
public class User implements Serializable(
//⽤户ID
private int id;
//⽤户姓名
private String username;
//⽤户性别
private String sex; }
开启了⼆级缓存后,还需要将要缓存的pojo实现Serializable接⼝,为了将缓存数据取出执⾏反序列化操
作,因为⼆级缓存数据存储介质多种多样,不⼀定只存在内存中,有可能存在硬盘中,如果我们要再取
这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接⼝
4.测试
测试执⾏commit()操作,⼆级缓存数据清空
@Test public void testTwoCache(){ //根据 sqlSessionFactory 产⽣ session SqlSession sqlSession1 = sessionFactory.openSession(); SqlSession sqlSession2 = sessionFactory.openSession(); SqlSession sqlSession3 = sessionFactory.openSession(); String statement = "com.lagou.pojo.UserMapper.selectUserByUserld" ; UserMapper userMapper1 = sqlSession1.getMapper(UserMapper. class ); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper. class ); UserMapper userMapper3 = sqlSession2.getMapper(UserMapper. class ); //第⼀次查询,发出sql语句,并将查询的结果放⼊缓存中 User u1 = userMapperl.selectUserByUserId( 1 ); System.out.println(u1); sqlSessionl .close(); //第⼀次查询完后关闭sqlSession //执⾏更新操作,commit() u1.setUsername( "aaa" ); userMapper3.updateUserByUserId(u1); sqlSession3.commit(); //第⼆次查询,由于上次更新操作,缓存数据已经清空(防⽌数据脏读),这⾥必须再次发出sql语 User u2 = userMapper2.selectUserByUserId( 1 ); System.out.println(u2); sqlSession2.close(); }
二级缓存整合redis
1.pom文件
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version> </dependency>
2.映射配置文件Mappe.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.lagou.mapper.IUserMapper"> <cache type="org.mybatis.caches.redis.RedisCache" /> <select id="findAll" resultType="com.lagou.pojo.User" useCache="true"> select * from user </select>
或者用注解的方式
@CacheNamespace(implementation = RedisCache.class) public interface IUserMapperRedisCache { /* 单注解增删改查 */ @Select({"select * from user where id = #{id}"}) public User findUserById(Integer id); //更新用户 @Update("update user set username = #{username} where id = #{id}") public void updateUser(User user); }
3.redis.properties
redis.host=localhost
redis.port=6379
redis.connectionTimeout=5000
redis.password=
redis.database=0
4.测试