MyBatis第二天01
1.高级结果映射
1.1根据视频案例,分析表之间的关系
数据模型分析
1、 明确每张表存储的信息
2、 明确每张表中关键字段(主键、外键、非空)
3、 明确数据库中表与表之间的外键关系
4、 明确业务中表与表的关系(建立在具体的业务)
所含的表为:
用户表
订单表
订单明细表
商品表
并且在程序中进行映射时必须要有对应的JavaBean
根据上面分析的数据模型我们来讨论高级映射:所谓高级映射也就是表(JavaBean)之间关联映射 :
分为:一对一、一对多、多对多
1.2一对一映射
1.2.1一对一映射之使用resultType
需求:通过订单id查询用户信息(主表为订单表,从表为用户表)
因为我们使用的是resultType我们只能返回一个映射类型我们通过继承Order来创建一个OrderExt扩展类并在里面添加用户信息。注意关注JavaBean的代码
sql:SELECT orders.`id`,orders.`user_id`,orders.`number`,user.`username`, user.`sex` FROM orders,user WHERE orders.`user_id` = user.`id`
重点在于Javabean与映射文件
①编写全局配置文件SqlMapCongfig.xml文件配置与上一篇文章的配置方式一致
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 7 <properties resource="db.properties"></properties> 8 9 <!-- 自定义别名 --> 10 <typeAliases> 11 <!--批量自定义别名 --><!--<推荐>批量自定义别名 name:批量指定po类别名的包名 使用:别名即类名,且不区分大小写--> 12 <package name="com.itheima.mybatis.po.order" /> 13 <package name="com.itheima.mybatis.po.user" /> 14 </typeAliases> 15 16 17 <!-- 配置环境信息 --> 18 <environments default="development"> 19 <!-- 环境配置必须要配置事务和数据源 --> 20 <environment id="development"> 21 <!-- 配置JDBC事务控制,由mybatis控制 --> 22 <transactionManager type="JDBC"></transactionManager> 23 <!--配置数据源 这里采用的是mybatis连接池 --> 24 <dataSource type="POOLED"> 25 <property name="driver" value="${db.driver}"/> 26 <property name="url" value="${db.url}"/> 27 <property name="username" value="${db.username}"/> 28 <property name="password" value="${db.password}"/> 29 </dataSource> 30 </environment> 31 </environments> 32 <mappers> 33 <!-- 批量加载映射文件 34 指定一个路径下的所有映射文件: 35 限定要求:1.接口或类需要与映射文件名称相同 2.接口或类需要和映射文件放在同一个目录下--> 36 <package name="com.itheima.mybatis.mapper"/> 37 </mappers> 38 </configuration>
②编写Mapper接口
1 public interface OrdersMapper { 2 //OrdersExt为订单类的子类也叫做扩展类 3 public List<OrdersExt> findOrdersAndUser(); 4 public List<OrdersExt> findOrdersAndUserRstMap(); 5 public List<OrdersExt> findOrdersAndDetailRstMap(); 6 public List<User> findOrderAndItemsRstMap(); 7 }
③OrderExt扩展类编写
1 public class OrdersExt extends Orders { 2 //添加User类属性username sex 3 private String username; 4 5 private String sex; 6 7 //这里省略了getset方法 8 @Override 9 public String toString() { 10 return "OrdersExt [username=" + username + ", sex=" + sex + ", user=" + user + ", detailList=" + detailList 11 + "]"; 12 } 13 14 15 }
④Ordermapper映射文件编写
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.itheima.mybatis.mapper.OrdersMapper"> 6 <!--这里所有类都使用了别名,别名统一配置在SqlMapConfig.xml中进行配置 --> 7 <!-- 订单和用户之间一对一映射,用resultType实现 订单对于用户是一对一 --> 8 <mapper> 9 <select id="findOrdersAndUser" resultType="OrdersExt"> 10 SELECT 11 orders.`id`, 12 orders.`user_id`, 13 orders.`number`, 14 user.`username`, 15 user.`sex` 16 FROM USER,orders 17 WHERE orders.`user_id`=user.`id` 18 </select> 19 </mapper>
⑤测试代码
1 public class OrderMapperTest { 2 private SqlSessionFactory sqlSessionFactory; 3 @Before 4 public void setUp() throws Exception { 5 String resource = "SqlMapConfig.xml"; 6 InputStream inputStream =Resources.getResourceAsStream(resource); 7 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 } 9 10 @Test 11 public void testFindOrdersAndUser() { 12 SqlSession sqlSession =sqlSessionFactory.openSession(); 13 OrdersMapper ordersMapper =sqlSession.getMapper(OrdersMapper.class); 14 List<OrdersExt> list=ordersMapper.findOrdersAndUser(); 15 //System.out.println(list.get(0).getId()); 16 System.out.println(list); 17 sqlSession.close(); 18 } 19 }
测试结果为
1.2.2一对一映射之使用resultMap
resultMap是使用的更为详细的映射方式,可以进行关联查询。简单来说 例如你想查询员工信息,而员工信息里面对应着一个部门的id信息,也就是在员工表中存在一个外键来关联部门。这个时候我们希望在查询员工的时候把部门信息也查询出来这个时候我们要用到resultMap
一个订单对应一个用户所以订单对用户是一对一
修改订单PO类关联用户类
1 public class OrdersExt extends Orders {//这里是继承里订单类 2 //添加User类属性username sex 3 private String username; 4 5 private String sex; 6 7 //添加用户User类完成一对一 8 private User user; 9 //省略了getset方法 10 11 }
对应mapper接口:OrderMapper
1 public interface OrdersMapper { 2 3 public List<OrdersExt> findOrdersAndUser(); 4 public List<OrdersExt> findOrdersAndUserRstMap(); 5 public List<OrdersExt> findOrdersAndDetailRstMap(); 6 public List<User> findOrderAndItemsRstMap(); 7 }
OrderMapper.xml的一对一查询之resultMap的使用
注意一对一关联:使用<association ></association> 标签代表一对一的嵌套关联写在resultMap标签内
<!--订单和用户之间一对一映射,用resultMap实现 订单对于用户是一对一 id:定义resultMap的唯一标识 type:映射的pojo类--> <!-- 配置resultMap id标签用于设置表中唯一主键 result标签映射结果集的普通列 column:查询表的列名 property:和映射对象属性名一致--> <resultMap type="OrdersExt" id="OrdersAndUserRstMap"> <!-- 订单信息 --> <id column="id" property="id"/> <result column="user_id" property="userId"/> <result column="number" property="number"/> <!-- 用户信息(一对一)--> <!-- association 用来映射一对一关系 property关联信息查询的属性结果将要映射的扩展类中的对象属性名称--> <!-- 在关联映射中最好加上id标签,不写不会报错但是会影响性能 user_id为外键所以也是user表的主键所以这样引用 --> <association property="user" javaType="com.itheima.mybatis.po.user.User"> <id column="user_id" property="id"/> <result column="username" property="username"/> <result column="sex" property="sex"/> </association> </resultMap> <select id="findOrdersAndUserRstMap" resultMap="OrdersAndUserRstMap"> <!-- sql语句不变 --> SELECT orders.`id`, orders.`user_id`, orders.`number`, user.`username`, user.`sex` FROM USER,orders WHERE orders.`user_id`=user.`id` </select>
测试代码:
1 public class OrderMapperTest { 2 private SqlSessionFactory sqlSessionFactory; 3 @Before 4 public void setUp() throws Exception { 5 String resource = "SqlMapConfig.xml"; 6 InputStream inputStream =Resources.getResourceAsStream(resource); 7 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 } 9 @Test 10 public void testFindOrdersAndUserRstMap() { 11 SqlSession sqlSession =sqlSessionFactory.openSession(); 12 OrdersMapper ordersMapper =sqlSession.getMapper(OrdersMapper.class); 13 List<OrdersExt> list=ordersMapper.findOrdersAndUserRstMap(); 14 15 System.out.println(list); 16 sqlSession.close(); 17 } 18 }
1.3一对多映射
根据上面的图片分析
一个订单对应多个订单明细 所以订单对订单明细为一对多
PO类的关联改写 OrederExt
1 public class OrdersExt extends Orders { 2 //添加User类属性username sex 3 private String username; 4 5 private String sex; 6 //添加用户User类完成一对一 7 private User user; 8 //添加订单明细一对多 9 private List<Orderdetail> detailList; 10 //此处省略getset方法 11 }
mapper接口编写同上省略(OrderMpper)
mapper映射文件:
<collection property="detailList" ofType="com.itheima.mybatis.po.order.Orderdetail"></collection >用来表示一对多
注意这里我们使用了一个继承属性extends可以重用上一个resultMap中的信息extends=“上一个resultMap的id名称”
1 <resultMap type="OrdersExt" id="OrdersAndDetailRstMap" extends="OrdersAndUserRstMap"> 2 <!-- 订单信息 上面已经配置过订单信息用户信息 我们可以继承--> 3 <!-- 用户信息 --> 4 5 <!-- 订单明细 property:对象的属性名 ofType映射的对象类型 --> 6 <collection property="detailList" ofType="com.itheima.mybatis.po.order.Orderdetail"> 7 <id column="detailId" property="id"/> 8 <result column="items_id" property="itemsId"/> 9 <result column="items_num" property="itemsNum"/> 10 </collection> 11 </resultMap> 12 <!--订单对订单明细的一对多关联查询 orderAndDetailRstMap --> 13 <select id="findOrdersAndDetailRstMap" resultMap="OrdersAndDetailRstMap"> 14 SELECT 15 orders.`id`, 16 orders.`user_id`, 17 orders.`number`, 18 user.`username`, 19 user.`sex`, 20 orderdetail.`id` detailId, 21 orderdetail.`items_id`, 22 orderdetail.`items_num` 23 FROM USER,orders,orderdetail 24 WHERE orders.`user_id`=user.`id` 25 AND orderdetail.`orders_id`=orders.`id`; 26 </select>
测试代码:
1 public class OrderMapperTest { 2 private SqlSessionFactory sqlSessionFactory; 3 @Before 4 public void setUp() throws Exception { 5 String resource = "SqlMapConfig.xml"; 6 InputStream inputStream =Resources.getResourceAsStream(resource); 7 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 } 9 @Test 10 public void testFindOrdersAndDetailRstMap() { 11 SqlSession sqlSession =sqlSessionFactory.openSession(); 12 OrdersMapper ordersMapper =sqlSession.getMapper(OrdersMapper.class); 13 List<OrdersExt> list=ordersMapper.findOrdersAndDetailRstMap(); 14 15 System.out.println(list); 16 sqlSession.close(); 17 }
1.4多对多映射
订单和商品是属于多对多关系,但是关联查询中表示多对多关联需要间接去表示 这里order为主表Order为主类
订单对明细是一对多,明细对商品是一对一这样我们可以间接嵌套表示出商品的信息
order-->orderdetail
1 public class Orders { 2 private Integer id; 3 4 private Integer userId; 5 6 private String number; 7 8 private Date createtime; 9 10 private String note; 11 12 private List<Orderdetail> detailList; 13 //省略getset方法和toString方法 14 15 }
orderdetial-->Item
1 public class Orderdetail { 2 private Integer id; 3 4 private Integer ordersId; 5 6 private Integer itemsId; 7 8 private Integer itemsNum; 9 10 11 //商品信息 订单明细对应商品信息是一对一 12 private Items items; 13 //省略getset方法和toString方法 14 15 }
mapper接口为OrderMapper接口同上
mapper映射文件OrderMapper.xml
1 <!-- 这里的需求发生变化 需求: 查询用户所买的商品 2 分析:用户和商品之间没有直接关联,但是有着多对多关系,通过订单到明细最后再到具体商品 3 主信息为用户User所以这里映射的是User类 当映射种类发生变化后就不能继承上面的其他resultMap隐射 --> 4 <resultMap type="com.itheima.mybatis.po.user.User" id="OrderAndItemsRstMap"> 5 <!-- 用户信息--> 6 <id column="user_id" property="id" /> 7 <result column="username" property="username"/> 8 <result column="sex" property="sex"/> 9 <!-- 订单信息 (用户对订单是一对多) 10 collection标签描述一对多 property表示User对象中关联的多方的属性名也就是ordersList ofType表示所要映射的对象类型 --> 11 <collection property="ordersList" ofType="com.itheima.mybatis.po.order.Orders"> 12 <id column="id" property="id"/> 13 <id column="user_id" property="userId"/> 14 <id column="number" property="number"/> 15 <!-- 订单明细(订单对订单明细也是一对多) --> 16 <collection property="detailList" ofType="com.itheima.mybatis.po.order.Orderdetail"> 17 <id column="detailId" property="id"/> 18 <id column="items_id" property="itemsId"/> 19 <id column="items_num" property="itemsNum"/> 20 <!-- 商品信息 (订单明细对商品信息是一对一) 21 association用来描述一对一添加该标签后记得在po类中进行相应的修改javaType用来映射指定的java类型--> 22 <association property="items" javaType="com.itheima.mybatis.po.item.Items" > 23 <id column="items_id" property="id"/> 24 <id column="name" property="name"/> 25 <id column="price" property="price"/> 26 </association> 27 </collection> 28 </collection> 29 30 31 32 </resultMap> 33 <!-- 订单对商品信息 的多对多关联查询OrderAndItemsRstMap--> 34 <select id="findOrderAndItemsRstMap" resultMap="OrderAndItemsRstMap"> 35 SELECT 36 orders.`id`, 37 orders.`user_id`, 38 orders.`number`, 39 user.`username`, 40 user.`sex`, 41 orderdetail.`id` detailId, 42 orderdetail.`items_id`, 43 orderdetail.`items_num`, 44 items.`name`, 45 items.`price` 46 FROM USER,orders,orderdetail,items 47 WHERE orders.`user_id`=user.`id` 48 AND orderdetail.`orders_id`=orders.`id` 49 AND orderdetail.`items_id`=items.`id` 50 </select> 51
测试代码:
1 public class OrderMapperTest { 2 private SqlSessionFactory sqlSessionFactory; 3 @Before 4 public void setUp() throws Exception { 5 String resource = "SqlMapConfig.xml"; 6 InputStream inputStream =Resources.getResourceAsStream(resource); 7 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 } 9 @Test 10 public void testFindOrdersAndItemsRstMap() { 11 SqlSession sqlSession =sqlSessionFactory.openSession(); 12 OrdersMapper ordersMapper =sqlSession.getMapper(OrdersMapper.class); 13 List<User> list=ordersMapper.findOrderAndItemsRstMap(); 14 15 System.out.println(list); 16 sqlSession.close(); 17 } 18 }
1.5延迟加载
延迟加载在关联查询的时候可以体现出来,但是延迟加载在全局配置文件中有一个设置 也就是在settings标签里面打开懒加载开关默认是积极的懒加载,我们将他设置为false
关于全局配置文件懒加载配置如下:这里我们省略了其他配置信息
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 7 <properties resource="db.properties"></properties> 8 <!-- 设置懒加载 此处省略其他配置信息 --> 9 <settings> 10 <!-- 开启懒加载默认值为true --> 11 <setting name="lazyLoadingEnabled" value="true"/> 12 <!-- 开启积极懒加载默认值为true --> 13 <setting name="aggressiveLazyLoading" value="false"/> 14 </settings> 15 </configuration>
关于懒加载我们使用一对一关联查询来测就可以其他配置信息不变
测试代码:当我们调用用户信息时才回去查询(这里省略了@Before的前置代码见上)
1 @Test 2 public void testFindOrderAndUserLazyLoading() { 3 SqlSession sqlSession =sqlSessionFactory.openSession(); 4 OrdersMapper ordersMapper =sqlSession.getMapper(OrdersMapper.class); 5 List<OrdersExt> list=ordersMapper.findOrderAndUserLazyLoading(); 6 7 for (OrdersExt ordersExt : list) { 8 System.out.println(ordersExt.getUser()); 9 } 10 System.out.println(list); 11 sqlSession.close(); 12 }
1.6查询缓存
mybatis缓存的理解
一级缓存存在于Session中
二级缓存存在于Mapper中
1.6.1mybatis一级缓存
一级缓存是mybatis默认开启的不用手动打开
一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。
缓存原理:(可以减少数据库访问来达到高效率)
所以只要是进行读操作,mybatis会默认去缓存区去寻找相对应的数据,而进行修改,添加,删除等操作时也会默认清空缓存区,当查询方法在增删改方法后执行那么一定是从数据库去查,由上图,如果第一次查询与第二次查询之间没有增删改操作时那么第二次查询会走缓存区
测试代码如下:
1 //根据用户用户id查询用户来验证一级缓存 2 @Test 3 public void testOneLevelCache() { 4 SqlSession sqlSession=sqlSessionFactory.openSession(); 5 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 6 //验证方法,查看是否只打印了一条sql语句,如果是就启用了一级缓存 7 User user1 = userMapper.findUserById(1); 8 System.out.println(user1); 9 //当执行插入操作时会清空一级缓存 验证方法可以查看进行插入操作之后是不是又打印了SQL语句如果是那么一级缓存被清空 10 // Integer result = userMapper.insertUser(user1); 11 // sqlSession.commit(); 12 // System.out.println(result); 13 14 User user2 = userMapper.findUserById(1); 15 System.out.println(user2); 16 sqlSession.close(); 17 }
1.6.2mybatis二级缓存
(如果使用第三方缓存框架):
第三方缓存框架首先需要导入整合jar包
ehcache-core-2.6.5.jar
mybatis-ehcache-1.0.2.jar
二级缓存是手动开启的不是默认的,我们需要手动在全局配置文件中加入settings标签设置,1.全局配置文件中配置2.还需要配置mapper映射文件指定哪些方法需要开启二级缓存3.查询的对象需要被序列化实现Serializable
二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是二级缓存区域。二级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。二级缓存中的value,就是查询出的结果对象。多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存原理:
1.全局配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 7 <properties resource="db.properties"></properties> 8 <!-- 设置懒加载 --> 9 <settings> 10 <!-- 开启懒加载默认值为true --> 11 <setting name="lazyLoadingEnabled" value="true"/> 12 <!-- 开启积极懒加载默认值为true --> 13 <setting name="aggressiveLazyLoading" value="false"/> 14 <!--这是一个总开关 开启二级缓存默认是不开启的 --> 15 <setting name="cacheEnabled" value="true"/> 16 </settings> 17 <!--其他配置在此处省略没写--> 18 </configuration>
2.mapper映射文件如下:(mybatis毕竟不是缓存框架所以项目中一般使用ehcache来实现二级缓存)
如果使用的ehcache的话需要配置配置信息:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" updateCheck="false"> <!-- path:缓存数据要存放在磁盘的地址 --> <diskStore path="F:Program FilesEhcachedevelopehcache"/> <!-- diskStore:指定数据在磁盘中的存储位置。 defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 以下属性是必须的: maxElementsInMemory - 在内存中缓存的element的最大数目 maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大 eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大 timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) --> <!-- 默认配置在ehcache文件夹下有一个ehcache-failsafe.xml xml配置信息可以从里面拷 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <!-- <persistence strategy="localTempSwap"/> --> </defaultCache> </ehcache>
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.itheima.mybatis.mapper.UserMapper"> <!-- 开启二级缓存的分开关 type:指定类型 如果是 PerpetualCache则可以不写默认就是它--> <!-- <cache type="org.apache.ibatis.cache.impl.PerpetualCache"/> --> <!-- 使用第三方支持的二级缓存ehcache 需要导入两个jar包,一个ehcache一个mybatis整合包--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> <!-- 通过id查询用户 --> <!-- 如果想要在指定查询中禁用二级缓存那么需要在select标签中加入 useCache="false" 默认是true--> <!-- <select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.user.User" useCache="false">...</select> --> <!-- 如果想要在每次执行查询的时候不走一级缓存或者二级缓存 那么可以添加flushCache="true" 默认值为true,这样就可以清空缓存区和insert达到一样的效果 --> <!-- <select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.user.User" flushCache="true"> --> <select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.user.User"> SELECT * FROM USER WHERE id=#{id} </select> <insert id="insertUser" parameterType="com.itheima.mybatis.po.user.User"> INSERT INTO USER (username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address}) </insert> </mapper>
3.po类实现序列化:
1 public class User implements Serializable{ 2 private int id; 3 private String username;// 用户姓名 4 private String sex;// 性别 5 private Date birthday;// 生日 6 private String address;// 地址 7 8 //订单信息 用户对订单信息是一对多 9 private List<Orders> ordersList; 10 11 //此处省略getset方法 12 }
测试代码:
1 /*一级缓存是默认开启 2 * 二级缓存需要手动开启:1.(总开关)需要在全局配置文件中配置setting标签 <setting name="CacheEnabled" value="true"/> 3 * 2.(分开关)在mapper映射文件中需要配置<cache/>标签 4 * 3.查询得到的对象要序列化 5 */ 6 7 //二级缓存验证 8 @Test 9 public void testTwoLevelCache() { 10 SqlSession sqlSession1=sqlSessionFactory.openSession(); 11 SqlSession sqlSession2=sqlSessionFactory.openSession(); 12 SqlSession sqlSession3=sqlSessionFactory.openSession(); 13 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); 14 UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); 15 UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class); 16 17 //验证方法,关闭了查询的sqlSession1如果sqlSession2查询时没有走数据库(没有打印sql语句)即启用了二级缓存 18 User user1 = userMapper1.findUserById(1); 19 System.out.println(user1); 20 sqlSession1.close(); 21 //当进行数据增删改时会清空二级缓存 验证方法可以查看进行插入操作之后是不是又打印了SQL语句如果是那么一级缓存被清空 22 /*Integer result = userMapper3.insertUser(user1); 23 sqlSession3.commit(); 24 System.out.println(result);*/ 25 sqlSession3.close(); 26 27 User user2 = userMapper2.findUserById(1); 28 System.out.println(user2); 29 sqlSession2.close();; 30 }
现象描述:也就是在进行其sqlSession进行增删改操作时会清空二缓存区,但要是没有经行增删改操作时,第一个sqlSession1查询的数据会放入二级缓存,当sqlSession2去访问相同时就会去二缓存区去拿
附上:
禁止缓存:
1 <!-- 如果想要在指定查询中禁用二级缓存那么需要在select标签中加入 useCache="false" 默认是true--> 2 <select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.user.User" useCache="false"> 3 SELECT * FROM USER WHERE id=#{id} 4 </select>
刷新缓存:
1 <!-- 如果想要在每次执行查询的时候不走一级缓存或者二级缓存 那么可以添加flushCache="true" 默认值为true,这样就可以清空缓存区和insert达到一样的效果 --> 2 <select id="findUserById" parameterType="int" resultType="com.itheima.mybatis.po.user.User" flushCache="true"> 3 SELECT * FROM USER WHERE id=#{id} 4 </select>
什么是分布式:
系统为了提高性能,通常会对系统采用分布式部署(集群部署方式)
二级缓存的局限性:
Mybatis二级缓存对细粒度的数据,缓存实现不好。
场景:对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都是最新的商品信息,此时如果使用二级缓存,就无法实现当一个商品发生变化只刷新该商品的缓存信息而不刷新其他商品缓存信息,因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。
解决此类问题,需要在业务层根据需要对数据有针对性的缓存。
比如可以对经常变化的 数据操作单独放到另一个namespace的mapper中。
1.6.3整合第三方缓存框架ehcache
第三方缓存框架首先需要导入整合jar包
ehcache-core-2.6.5.jar
mybatis-ehcache-1.0.2.jar
此处省略见上面(在上面我们已经顺带提了)
1.7spring整合mybatis
首先我们需要导入整合jar包
mybatis-spring-1.2.2.jar
我们需要将数据源还有事务全部交给spring配置,还有就是映射文件和全局配置文件还有接口都可以在spring中配置这样我们可以在mybatis映射文件中只需要配置简单的配置信息即可减轻了mybatis配置信息的负担
applicationContext.xml配置信息如下:(这里的applicationContext是复制ssm整合的和下面的那个不匹配)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xmlns:tx="http://www.springframework.org/schema/tx" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/tx 12 http://www.springframework.org/schema/tx/spring-tx.xsd 13 http://www.springframework.org/schema/aop 14 http://www.springframework.org/schema/aop/spring-aop.xsd"> 15 <!-- 注解开发第一步就是要扫描包下注解 --> 16 <context:component-scan base-package="com.itheima.ssm"></context:component-scan> 17 <!-- 配置spring的三个部分 --> 18 <!-- 数据源 --> 19 <context:property-placeholder location="classpath:jdbc.properties"/> 20 21 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 22 <property name="jdbcUrl" value="${jdbc.url}"></property> 23 <property name="driverClass" value="${jdbc.driver}"></property> 24 <property name="user" value="${jdbc.username}"></property> 25 <property name="password" value="${jdbc.password}"></property> 26 </bean> 27 <!-- Mybatis主键的整合:SqlSessionFactory的创建和Mapper接口的扫描 --> 28 <bean id="sqlSessionFactroy" class="org.mybatis.spring.SqlSessionFactoryBean"> 29 <property name="dataSource" ref="dataSource"></property> 30 <property name="configLocation" value="classpath:SqlMapConfig.xml"></property> 31 </bean> 32 33 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 34 <!--没有配置映射文件的位置那是因为接口和映射文件处于同一个包下--> 35 <property name="basePackage" value="com.itheima.ssm.dao.mapper"></property> 36 <!-- 配置工厂,如果只配置了一个工厂bean那么可以不用配置,默认就会调用这里我们还是写上 --> 37 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactroy"></property> 38 </bean> 39 40 41 <!-- 事务编写 数据源事务管理 一般预定俗成id名称为transactionManager--> 42 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 43 <property name="dataSource" ref="dataSource"></property> 44 </bean> 45 46 47 <!--配置通知 配置通知 管理的事务就是上面的数据源事务管理 关于切面编程在spring笔记中有详细介绍--> 48 <tx:advice id="txAdvice" transaction-manager="transactionManager" > 49 <tx:attributes> 50 <tx:method name="save*" propagation="REQUIRED"/> 51 <tx:method name="update*" propagation="REQUIRED"/> 52 <tx:method name="delete*" propagation="REQUIRED"/> 53 </tx:attributes> 54 </tx:advice> 55 56 <!-- 来拦截service 切面编程 其中pointcut是切入点 * com.ithiema.ssm.service.*.*(..) 57 翻译过来 就是任意修饰符 在com.ithiema.ssm.service包下任意类的的任意参数方法--> 58 <aop:config> 59 <aop:pointcut expression="execution(* com.ithiema.ssm.service.*.*(..))" id="MyPointcut"/> 60 <aop:advisor advice-ref="txAdvice" pointcut-ref="MyPointcut"/> 61 </aop:config>
这个下面的applicationContext.xml是匹配下面sm整合的:(因为这个上面没有添加事务没有上面这个详细)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/tx 12 http://www.springframework.org/schema/tx/spring-tx.xsd 13 http://www.springframework.org/schema/jdbc 14 http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> 15 <!-- 加载properties文件 --> 16 <context:property-placeholder location="db.properties"/> 17 18 <!-- 添加数据源 --> 19 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 20 <property name="driverClassName" value="${db.driver}"></property> 21 <property name="url" value="${db.url}"></property> 22 <property name="username" value="${db.username}"></property> 23 <property name="password" value="${db.password}"></property> 24 </bean> 25 <!-- UserDao的实现 --> 26 <bean id="userDao" class="com.itheima.ms.dao.impl.UserDaoImpl"> 27 <!-- 依赖注入sqlSessionFactory --> 28 <property name="sqlSessionFactory" ref="sqlSessionFactroy"></property> 29 </bean> 30 <!-- ==============mybatis整合spring============================= --> 31 32 <!-- 创建SqlSessionFactroyBean 这个类是由mybatis和spring的整合包提供的 --> 33 <bean id="sqlSessionFactroy" class="org.mybatis.spring.SqlSessionFactoryBean"> 34 <!-- 指定mybatis全局配置文件路径 --> 35 <property name="configLocation" value="mybatis/SqlMapConfig.xml"></property> 36 <!--指定数据源 --> 37 <property name="dataSource" ref="dataSource"></property> 38 </bean> 39 40 41 <!-- 配置单个mapper代理类 --> 42 <!-- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> 43 扫描指定包下的指定mapper接口 44 <property name="mapperInterface" value="com.itheima.ms.mapper.UserMapper"></property> 45 指定sqlSessionFactory,因为代理类是由它创建的 46 <property name="sqlSessionFactory" ref="sqlSessionFactroy"></property> 47 </bean> 48 --> 49 50 <!-- 批量配置mapper代理类,不需要指定id,默认为mapper接口名的首字母小写 --> 51 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 52 <!-- 指定扫描特定包下的mapper接口 --> 53 <property name="basePackage" value="com.itheima.ms.mapper"></property> 54 <!-- 指定sqlSessionFactory,但是批量配置mapper代理类时有点特殊。 55 当只配置了一个sqlSessionFactory时它会默认使用它所以是不需要配置的(当然指定也没错),但是有多个sqlSessionFactory时需要指定。 56 --> 57 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactroy"></property> 58 <!-- <property name="sqlSessionFactory" ref="sqlSessionFactroy"></property> --> 59 </bean> 60 </beans>
SqlMapConfig.xml配置信息如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <typeAliases > 7 <!-- 定义批量别名--> 8 <package name="com.itheima.ms.po"/> 9 </typeAliases> 10 <mappers> 11 <!-- 单个添加映射文件 resource表示一个路径--> 12 <mapper resource="com/itheima/ms/po/User.xml" /> 13 <!-- 批量添加映射文件 当spring和mybatis整合之后当实现mapper代理时 14 就不需要配置加载映射文件它会自动加载指定的xxxMapper.xml 和批量加载映射文件的限制一样, 15 需要名称一样并且在同一个包下 16 分析:因为在spring配置文件中 basePackage 值是一个包路径当它扫描时不仅扫描了接口也会将接口特定映射文件xml文件加载进去 17 所以必须在同一个包下才行 18 --> 19 <!-- <package name="com.itheima.ms.mapper"/> --> 20 </mappers> 21 </configuration>
1.8mybatis的逆向工程
首先我们需要导入逆向工程的相关jar包标红的为逆向工程jar包其他的是数据库驱动包和mybatis的核心包
mybatis-3.2.7.jar
mybatis-generator-core-1.3.2.jar
mysql-connector-java-5.1.46-bin.jar
正向工程是代码到数据表的映射过程,mybatis逆向工程是从数据库表到生成相应Mapper接口PO类到映射文件的过程
这里我们需要去参照官方文档:
F:Java黑马在线MyBatismybatis逆向工程mybatis-generator-core-1.3.2docs
unning
unning.html
这里代码是拷过来的:在逆向工程的包里面有一个文档文件running.html 打开点击Genenration 下的with java
我们通过使用java程序来执行mybatis逆向工程
执行的方法代码:Generator.java
1 public class Generator { 2 public static void main(String[] args) throws Exception{ 3 List<String> warnings = new ArrayList<String>(); 4 boolean overwrite = true; 5 //只有这里我们需要指定相应逆向工程配置文件路径,如果路径不匹配那么会报错 6 File configFile = new File("config/generatorConfig.xml"); 7 ConfigurationParser cp = new ConfigurationParser(warnings); 8 Configuration config = cp.parseConfiguration(configFile); 9 DefaultShellCallback callback = new DefaultShellCallback(overwrite); 10 MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); 11 myBatisGenerator.generate(null); 12 } 13
逆向工程需要配置信息:generatorConfig.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 代码是拷过来的:在逆向工程的包里面有一个文档文件running.html Configuration File Reference --> 3 <!DOCTYPE generatorConfiguration 4 PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" 5 "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> 6 7 <generatorConfiguration> 8 <context id="testTables" targetRuntime="MyBatis3"> 9 <commentGenerator> 10 <!-- 是否去除自动生成的注释 true:是 : false:否 --> 11 <property name="suppressAllComments" value="true" /> 12 </commentGenerator> 13 <!--数据库连接的信息:驱动类、连接地址、用户名、密码 --> 14 <jdbcConnection driverClass="com.mysql.jdbc.Driver" 15 connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" 16 password="123"> 17 </jdbcConnection> 18 <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 19 userId="yycg" password="yycg"> </jdbcConnection> --> 20 21 <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 22 和 NUMERIC 类型解析为java.math.BigDecimal --> 23 <javaTypeResolver> 24 <property name="forceBigDecimals" value="false" /> 25 </javaTypeResolver> 26 27 <!-- targetProject:生成PO类的位置 --> 28 <javaModelGenerator targetPackage="com.itheima.ms.po" 29 targetProject=".src"> 30 <!-- enableSubPackages:是否让schema作为包的后缀 --> 31 <property name="enableSubPackages" value="false" /> 32 <!-- 从数据库返回的值被清理前后的空格 --> 33 <property name="trimStrings" value="true" /> 34 </javaModelGenerator> 35 <!-- targetProject:mapper映射文件生成的位置 --> 36 <sqlMapGenerator targetPackage="com.itheima.ms.mapper" 37 targetProject=".src"> 38 <!-- enableSubPackages:是否让schema作为包的后缀 --> 39 <property name="enableSubPackages" value="false" /> 40 </sqlMapGenerator> 41 <!-- targetPackage:mapper接口生成的位置 --> 42 <javaClientGenerator type="XMLMAPPER" 43 targetPackage="com.itheima.ms.mapper" targetProject=".src"> 44 <!-- enableSubPackages:是否让schema作为包的后缀 --> 45 <property name="enableSubPackages" value="false" /> 46 </javaClientGenerator> 47 <!-- 指定数据库表 --> 48 <table tableName="items"></table> 49 <table tableName="orders"></table> 50 <table tableName="orderdetail"></table> 51 <table tableName="user"></table> 52 </context> 53 </generatorConfiguration>
运行主程序刷新浏览列表