概览 原生jdbc缺陷 原生jdbc十分繁琐,而且占位符不利于维护,缺点很明显
数据库使用时创建连接,不使用就释放,频繁的开启和关闭,十分浪费资源(使用数据库连接池管理连接) sql语句写在java代码中,不利于维护(将sql独立出来在xml文件中) preparedStatement
设置参数,十分繁琐且不利于维护(将参数以及占位符也配置在xml中)从resultSet中取得的数据需要遍历获得,很麻烦(将结果集映射成对象) 基本使用 mybatis配置
执行过程:
配置mybatis的配置文件(sqlMapConfig.xml) 通过配置文件,加载mybatis运行环境,创建SqlSessionFactory会话工厂 通过SqlSessionFactory创建SqlSession。SqlSession是一个面向用户接口(提供操作数据库方法) 调用sqlSession的方法去操作数据。sqlSession通过Executor去执行操作 释放资源,关闭sqlSession sqlMapConfig.xml配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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 > <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/mybatis_learn?characterEncoding=utf-8" /> <property name ="username" value ="root" /> <property name ="password" value ="xxx" /> </dataSource > </environment > </environments > </configuration >
sqlSession SqlSessionFactoryBuilder: 通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可 SqlSessionFactory: 通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory SqlSession SqlSession是一个面向用户(程序员)的接口,提供了很多操作数据库的方法 mybatis是使用sqlSession来操作数据库的,比较两种方法:
定义dao 原始的dao的实现(sqlSession方式) 定义interface
1 2 3 4 5 6 7 8 9 10 public interface UserDao { public User findUserById (int id) throws Exception; public List<User> findUserByName (String name) throws Exception; public void insertUser (User user) throws Exception; public void deleteUser (int id) throws Exception; }
dao的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class UserDaoImpl implements UserDao { private SqlSessionFactory sqlSessionFactory; public UserDaoImpl (SqlSessionFactory sqlSessionFactory) { this .sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById (int id) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("test.findUserById" ,id); sqlSession.close(); return user; } @Override public List<User> findUserByName (String name) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> list = sqlSession.selectList("test.findUserByName" , name); sqlSession.close(); return list; } @Override public void insertUser (User user) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("test.insertUser" , user); sqlSession.commit(); sqlSession.close(); } @Override public void deleteUser (int id) throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.delete("test.deleteUser" , id); sqlSession.commit(); sqlSession.close(); } }
这种方法存在大量的重复代码,调用sqlSession方法的时候会存在硬编码的问题
mapper代理的方法 写好mapper接口(相当于dao的接口)和mapper.xml映射文件,mybatis可以自动生成mapper接口的实现类对象
namespace为mapper映射的接口地址
1 <mapper namespace ="com.iot.mybatis.mapper.UserMapper" >
mapper.java接口中的方法名和mapper.xml中statement的id一致
mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致
1 2 3 <select id="findUserById" parameterType="int" resultType="com.iot.mybatis.po.User" > SELECT * FROM user WHERE id=#{value } </select >
mapper.java中对应的方法应该这么写:
1 public User findUserById (int id) throws Exception ;
在SqlMapConfig.xml中加载mapper
1 2 3 4 5 6 7 <mappers > <mapper resource ="mapper/UserMapper.xml" /> </mappers > 另外还有一种批量加载mapper方法: mybatis自动扫描包下边所有mapper接口进行加载<package name ="com.iot.mybatis.mapper" />
更多 mapper映射文件的使用,参考mybatis中文官网——Mapper XML文件
复杂查询 user表: id username ……..
oder表: id user_id number(订单编号)
oder_detail表: id orders_id items_id items_num
items表: id name price …….
一对一查询 实现用户表和订单表的查询,查询出某用户的订单信息
mapper.xml
1 2 3 4 5 6 7 8 9 10 11 <select id="findOrdersUser" resultType="com.iot.mybatis.po.OrdersCustom" > SELECT orders.*, user .username, user .sex, user .address FROM orders, user WHERE orders.user_id = user .id </select >
mapper.java
1 public List<OrdersCustom> findOrdersUser ()throws Exception ;
resultMap实现 使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。 mapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <resultMap type ="com.iot.mybatis.po.Orders" id ="OrdersUserResultMap" > <id column ="id" property ="id" /> <result column ="user_id" property ="userId" /> <result column ="number" property ="number" /> <result column ="createtime" property ="createtime" /> <result column ="note" property ="note" /> <association property ="user" javaType ="com.iot.mybatis.po.User" > <id column ="user_id" property ="id" /> <result column ="username" property ="username" /> <result column ="sex" property ="sex" /> <result column ="address" property ="address" /> </association > </resultMap > statement定义:<select id ="findOrdersUserResultMap" resultMap ="OrdersUserResultMap" > SELECT orders.*, user.username, user.sex, user.address FROM orders, user WHERE orders.user_id = user.id</select >
mapper.java
1 public List<Orders> findOrdersUserResultMap ()throws Exception ;
一对多查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT orders.*, user.username, user.sex, user.address, orderdetail.id as orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id FROM orders, user, orderdetail WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id
由于一个订单对应多个商品,那么订单的信息就是重复的,可以在order.java
中添加List<OrderDetail> orderDetail
属性,订单信息映射到order中,订单详情映射到orderDetail属性当中
使用resultMap来实现这个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // 查询映射<select id ="findOrdersAndOrderDetailResultMap" resultMap ="OrdersAndOrderDetailResultMap" > SELECT orders.*, user.username, user.sex, user.address, orderdetail.id orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id FROM orders, user, orderdetail WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id</select > // resultMap映射<resultMap type ="com.iot.mybatis.po.Orders" id ="OrdersAndOrderDetailResultMap" extends ="OrdersUserResultMap" > <collection property ="orderdetails" ofType ="com.iot.mybatis.po.Orderdetail" > <id column ="orderdetail_id" property ="id" /> <result column ="items_id" property ="itemsId" /> <result column ="items_num" property ="itemsNum" /> <result column ="orders_id" property ="ordersId" /> </collection > </resultMap >
多对多查询 在查询用户以及购买商品的信息,是多对多的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 SELECT orders.*, user.username, user.sex, user.address, orderdetail.id orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id, items.name items_name, items.detail items_detail, items.price items_price FROM orders, user, orderdetail, items WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
将查询结果映射到User类
User类中添加List<Order> orderList
来保存用户的订单信息
添加List<OrderDetail> orderDetail
来保存订单明细
在OrderDetail类中添加Items
属性,保存订单明细所对应的商品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <select id ="findUserAndItemsResultMap" resultMap ="UserAndItemsResultMap" > SELECT orders.*, user.username, user.sex, user.address, orderdetail.id orderdetail_id, orderdetail.items_id, orderdetail.items_num, orderdetail.orders_id, items.name items_name, items.detail items_detail, items.price items_price FROM orders, user, orderdetail, items WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id</select > <resultMap type ="com.iot.mybatis.po.User" id ="UserAndItemsResultMap" > <id column ="user_id" property ="id" /> <result column ="username" property ="username" /> <result column ="sex" property ="sex" /> <result column ="address" property ="address" /> <collection property ="ordersList" ofType ="com.iot.mybatis.po.Orders" > <id column ="id" property ="id" /> <result column ="user_id" property ="userId" /> <result column ="number" property ="number" /> <result column ="createtime" property ="createtime" /> <result column ="note" property ="note" /> <collection property ="orderdetails" ofType ="com.iot.mybatis.po.Orderdetail" > <id column ="orderdetail_id" property ="id" /> <result column ="items_id" property ="itemsId" /> <result column ="items_num" property ="itemsNum" /> <result column ="orders_id" property ="ordersId" /> <association property ="items" javaType ="com.iot.mybatis.po.Items" > <id column ="items_id" property ="id" /> <result column ="items_name" property ="name" /> <result column ="items_detail" property ="detail" /> <result column ="items_price" property ="price" /> </association > </collection > </collection > </resultMap >
缓存 数据库缓存可以减轻数据库压力,提高性能。mybatis提供一级缓存和二级缓存
根据上图可以看到一级缓存是sqlSession级别的缓存,不同sqlSession之间缓存的数据是互相不影响的
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
一级缓存 mybatis默认支持一级缓存,不需要配置,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Test public void testCache1 () throws Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user1 = userMapper.findUserById(1 ); System.out.println(user1); User user2 = userMapper.findUserById(1 ); System.out.println(user2); sqlSession.close(); }
二级缓存 开启二级缓存1 2 3 4 5 6 7 8 9 全局开启:<setting name ="cacheEnabled" value ="true" /> 某个mapper开启:<mapper namespace ="com.iot.mybatis.mapper.UserMapper" > <cache /> ......</mapper >
2.使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Test public void testCache2 () throws Exception { SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); SqlSession sqlSession3 = sqlSessionFactory.openSession(); UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); User user1 = userMapper1.findUserById(1 ); System.out.println(user1); sqlSession1.close(); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); User user2 = userMapper2.findUserById(1 ); System.out.println(user2); sqlSession2.close(); }
useCache和flushCache useCache 在statement中设置useCache=flase
可以禁用当前select语句的二级缓存,如果每次查询都需要是最新数据,需要设置useCache为false1 <select id ="findOrderListResultMap" resultMap ="ordersUserMap" useCache ="false" >
清空缓存 刷新缓存就是清空缓存。在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读 在statement设置flushCache = "true"
,默认情况也是true既刷新缓存,也可以设置成false不刷新缓存(可能引起脏读),如下:
1 <insert id ="insertUser" parameterType ="cn.itcast.mybatis.po.User" flushCache ="true" >
懒加载 参考资料