03_Mybatis
Mybatis事务控制
工程准备:新建一个Maven类型的module:实现Mybatis保存用户功能,便于基于该功能的代码程序说明Mybatis的事务控制
SqlMapConfig.xml中配置的type=“JDBC”:使用Jdbc的事务管理机制,connection的事务
Mybatis默认把Jdbc的事务自动提交给关闭了
Mybatis缓存
缓存只能对的是CRUD中的R(查询)
缓存需要进行维护(数据发生变化的时候,需要维护缓存),这样才能保持数据的一致性
工程准备:实现Mybatis根据id查询用户功能,便于基于该功能的代码程序说明Mybatis缓存
概述
网站性能优化第一定律:优先使用缓存机制(分布式redis缓存)。
Mybatis中的缓存
Mybatis提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。
Mybatis中缓存分为一级缓存,二级缓存。Mybatis的二级缓存机制存在诸多问题。无法在企业中开发中应用。
Mybatis的一级缓存是SqlSession级别(在同一个SqlSession持续期间有效)的缓存,是默认开启的
@Test
public void testQueryUserOrders() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
<!--一级缓存是SqlSession级别的,同一个SqlSession范围内执行相同的sql语句,会走一级缓存-->
sqlSession.close();
}
结果分析
虽然上面的代码我们查询中写了两次,但是只执行了一次数据库查询操作,这就是Mybatis提供给我们的一级缓存起作用了
因为一级缓存的存在,导致第二次查询同一id的记录是(同一条sql),并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询
第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库中查询信息。得到用户信息,将用户信息存储到一级缓存中,
如果sqlSession去执行commit操作(执行插入,更新,删除),清空sqkSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
一级缓存是SqlSession范围的缓存,当调用sqlSession的修改,添加,删除,commit(),close()等方法时候,就会清空一级缓存
Mybatis映射文件的Sql深入(动态sql机制)
优雅的帮助我们拼接sql语句(主要是标签是使用)
需求:根据用户性别和用户名称查询用户列表
if标签/where标签
List<User> queryUserByWhere(User user) throws Exception;
<!--根据用户性别和用户名查询user ,拼接sql语句的方式-->
<select id="queryUserByWhere" parameterType="user" resultType="user">
SELECT * FROM USER
<where><!--where会自动添加where关键字并且干掉紧接着的第一个and关键字或者or关键字-->
<!--if标签会帮我们进行判断,test判断表达式的真假-->
<if test="sex != null and sex != '' ">
AND sex = #{sex}
</if>
<if test="username != null and username != ''">
AND username=#{username}
</if>
</where>
</select>
/**
*使用if标签和where标签 动态SQL语句 拼接sql语句
*/
@Test
public void testQueryUserByWhere() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
// user.setUsername("张三");
user.setSex("1");
List<User> users = mapper.queryUserByWhere(user);
for (int i = 0; i < users.size(); i++) {
User user1 = users.get(i);
System.out.println(user);
}
sqlSession.close();
}
sql片段
<!--
sql片段
引用sql片段使用include标签,refid 只想sql片段的id 如果共享其他的mapper文件当中的sql片段,
只要在refid前面加上另一个mapper映射文件的namespace即可
-->
<sql id="commentSql">
id,username,sex,birthday,address
</sql>
foreach标签
List<User> queryUserByIdsList(List<Integer> list)throws Exception;
<select id="queryUserByIdsList" parameterType="list" resultType="user">
SELECT <include refid="commentSql"></include> FROM USER
<foreach collection="list" open="where id in(" close=")" separator="," item="item">
#{item}
</foreach>
</select>
/**
* 根据多个id来查询用户列表
*/
@Test
public void testQueryUserByIdsList() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
List<User> users = userMapper.queryUserByIdsList(list);
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
System.out.println(user);
}
sqlSession.close();
}
Mybatis的多表关联查询
多表关系分析
多表:至少2两张表
多表关系分析技巧:盯着一张表A的一条记录,从这一条记录出发,看这条记录在另外一张表B中可能有几条关联记录,如果只会有一条关联记录,那么从A到B就是一对一,如果可能有多条关联记录,那么从A到B就是一个一对多的关系。
多表关系:
一对一
一对多
User表 orders表
从表中有外键指向主表中的主键
多对多
User role
中间表
Userid roleid
多表关联的SQL语句表达
-
笛卡尔积
select * from user,orders
-
笛卡尔积 + where
select * from user u ,orders o where u.id = o.user_id
-
关联查询
内关联 inner join效果等同于笛卡尔积+where
外关联
left join
right join
一对一查询
Mybatis 帮我们做的事:
- sql语句是用户自己写的,Mybatis框架只是帮我们执行
- 封装结果集
需求:查询订单表全部数据,关联查询出订单对应的用户数据(username address)
Mapper映射文件
<select id="queryOrdersUser" resultMap="ordersUserResultMap">
SELECT
o.`id`,
o.`user_id`,
o.`number`,
o.`createtime`,
o.`note`,
u.`username`,
u.`address`
FROM
orders o
LEFT JOIN user u
ON o.`user_id` = u.`id`
</select>
<resultMap id="ordersUserResultMap" type="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"/>
<!--association:关联的意思,用于一对一关联封装数据
property:一对一关联后封装成的数据所对应的属性名
javaType:属性的类型
-->
<association property="user" javaType="user">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
</association>
</resultMap>
测试程序
@Test
public void testQueryOrdersUser()throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper ordersMapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> ordersList = ordersMapper.queryOrdersUser();
if(ordersList != null && ordersList.size() > 0) {
for (int i = 0; i < ordersList.size(); i++) {
Orders orders = ordersList.get(i);
System.out.println(orders);
}
}
sqlSession.close();
}
一对多查询
需求:查询全部用户数据,关联查询出订单数据
Mapper映射文件
<!--一对多用例-->
<select id="queryUserOrders" resultMap="userOrdersResultmap">
SELECT
u.`id`,
u.`username`,
u.`sex`,
u.`birthday`,
u.`address`,
o.`id` oid,
o.`user_id`,
o.`number`,
o.`createtime`,
o.`note`
FROM
user u
LEFT JOIN orders o
ON u.`id` = o.`user_id`
</select>
<resultMap id="userOrdersResultmap" type="user">
<!--两部分数据:用户+订单-->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<!--一对多使用collection标签
property:封装成对应的属性的属性名
ofType:因为此时属性是一个list集合,collection已经表明了集合的意思,最重要的指定list里面的泛型类型,
所以使用ofType
-->
<collection property="ordersList" ofType="orders">
<id column="oid" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
</collection>
</resultMap>