MyBatis映射文件中除了
、
、
、外,还有一些标签可以使用:
标签的作用的自定义映射关系。
MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:
当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。
此时有两种解决方案:
Sql语句的查询字段起与POJO属性相同的别名。
<select id="findAll" resultType="com.mybatis.pojo.Teacher">
select tid as id,tname as teacherName from teacher;
select>
自定义映射关系
自定义映射关系:
<resultMap id="teacherMapper" type="com.mybatis.pojo.Teacher">
<id property="id" column="tid">id>
<result property="teacherName" column="tname">result>
resultMap>
标签中,使用resultMap
属性代替resultType
属性,使用自定义映射关系。<select id="findAll" resultMap="teacherMapper">
select * from teacher
select>
用来定义可重用的Sql片段,通过
引入该片段。如:Sql语句的查询字段起与POJO属性相同的别名,该Sql片段就可以重用。
<sql id="selectAllField">
select tid as id,tname as teacherName
sql>
<select id="findAll" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField">include>
from teacher;
select>
<select id="findById" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField">include>
from teacher where tid = #{id}
select>
在Mybatis映射文件中尽量不要使用一些特殊字符,如:<
,>
等。
我们可以使用符号的实体来表示:(带后面的分号)
符号 | 实体 |
---|---|
< | & lt; |
> | & gt; |
& | & amp; |
‘ | & apos; |
“ | & quot; |
如:
<select id="findById2" resultType="com.mybatis.pojo.Teacher">
<include refid="selectAllField">include>
from teacher where tid > #{id}
select>
一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。
标签内的Sql片段在满足条件后才会添加,用法为:
。例如:根据不同条件查询用户:
持久层接口添加方法
// 用户通用查询
List<User> findByCondition(User user);
映射文件添加标签
<select id="findByCondition" parameterType="com.mybatis.pojo.User" resultType="com.mybatis.pojo.User">
select * from user where 1 = 1
<if test="username != null and username.length() != 0">
and username like #{username}
if>
<if test="sex != null and sex.length() != 0">
and sex = #{sex}
if>
<if test="address != null and address.length() != 0">
and address = #{address}
if>
select>
编写测试方法
@Test
public void testFindByCondition(){
User user = new User();
List<User> users1 = userMapper2.findByCondition(user);
//users1.forEach(System.out::println);
user.setUsername("%张三%");
List<User> users2 = userMapper2.findByCondition(user);
users2.forEach(System.out::println);
user.setAddress("北京");
List<User> users3 = userMapper2.findByCondition(user);
users3.forEach(System.out::println);
}
if中的条件不能使用&&/||,而应该使用and/or
if中的条件可以直接通过属性名获取参数POJO的属性值,并且该值可以调用方法。
where后为什么要加1=1?
任意条件都可能拼接到Sql中。如果有多个条件,从第二个条件开始前都需要加And关键字。加上1=1这个永久成立的条件,就不需要考虑后面的条件哪个是第一个条件,后面的条件前都加And关键字即可。
可以代替sql中的where 1=1 和第一个and,更符合程序员的开发习惯,使用
后的映射文件如下:
<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
select * from user
<where>
<if test="username != null and username.length() != 0">
username like #{username}
if>
<if test="sex != null and sex.length() != 0">
and sex = #{sex}
if>
where>
select>
标签用在update语句中。借助
,可以只对有具体值的字段进行更新。
会自动添加set关键字,并去掉最后一个if语句中多余的逗号。
<update id="update" parameterType="com.mybatis.user.User">
update user
<set>
<if test="username != null and username.length() > 0">
username = #{username},
if>
<if test="sex != null and sex.length() > 0">
sex = #{sex},
if>
set>
<where>
id = #{id}
where>
update>
这些标签表示多条件分支,类似JAVA中的switch...case
。
类似switch
,
类似case
,
类似default
,用法如下:
<select id="findByCondition" resultType="com.mybatis.user.User" parameterType="com.mybatis.user.User">
select * from user
<where>
<choose>
<when test="username.length() < 5">
username like #{username}
when>
<when test="username.length() < 10">
username = #{username}
when>
<otherwise>
id = 1
otherwise>
choose>
where>
select>
这段代码的含义为:用户名<5时使用模糊查询,用户名>=5并且<10时使用精确查询,否则查询id为1的用户
类似JAVA中的for循环,可以遍历集合或数组。
有如下属性:
我们使用
遍历数组进行批量删除。
持久层接口添加方法
void deleteBatch(int[] ids);
映射文件添加标签
<delete id="deleteBatch" parameterType="int">
delete from user
<where>
<foreach open="id in(" close=")" separator="," collection="array" item="id" >
#{id}
foreach>
where>
delete>
编写测试方法
@Test
public void testDeleteBatch(){
int[] ids = {9,11};
userMapper.deleteBatch(ids);
session.commit();
}
遍历List和Set的方法是一样的,我们使用
遍历List进行批量添加。
持久层接口添加方法
void insertBatch(List<User> users);
映射文件添加标签
<insert id="insertBatch" parameterType="com.mybatis.user.User">
insert into user values
<foreach collection="list" item="user" separator=",">
(null ,#{user.username},#{user.sex},#{user.address})
foreach>
insert>
编写测试方法
@Test
public void testInsertBatch(){
User user1 = new User("程序员1", "男", "北京");
User user2 = new User("程序员2", "女", "上海");
List<User> users = new ArrayList();
users.add(user1);
users.add(user2);
userMapper2.insertBatch(users);
session.commit();
}
我们使用
遍历Map进行多条件查询。
持久层接口添加方法
/**
* 多条件查询
* @param map 查询的条件键值对 键:属性名 值:属性值
* @return
*/
List<User> findUser(@Param("queryMap") Map<String,Object> map);
映射文件添加标签
<select id="findUser" parameterType="map" resultType="com.mybatis.pojo.User">
select * from user
<where>
<foreach collection="queryMap" separator="and" index="key" item="value">
${key} = #{value}
foreach>
where>
select>
编写测试方法
@Test
public void testFindUser(){
Map<String,Object> queryMap = new HashMap();
queryMap.put("sex","男");
queryMap.put("address","北京");
List<User> users = userMapper2.findUser(queryMap);
users.forEach(System.out::println);
}
缓存是内存当中一块存储数据的区域,目的是提高查询效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。
什么是缓存?
存在于内存中的一块数据。
缓存有什么作用?
减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。
什么样的数据使用缓存?
经常查询但不常改变的,改变后对结果影响不大的数据。
MyBatis缓存分为哪几类?
一级缓存和二级缓存
如何判断两次Sql是相同的?
- 查询的Sql语句相同
- 传递的参数值相同
- 对结果集的要求相同
- 预编译的模板Id相同
@Test
public void testCache1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
// 使用同一个SqlSession查询
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
@Test
public void testCache2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
// 使用不同的SqlSession查询
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
进行以下操作可以清空MyBatis一级缓存:
SqlSession
调用close()
:操作后SqlSession对象不可用,该对象的缓存数据也不可用。SqlSession
调用clearCache()
/commit()
:操作会清空一级缓存数据。SqlSession
调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。@Test
public void testCache3() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper mapper1 = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1.hashCode());
// session.close();
// session.clearCache();
// session.commit();
mapper1.delete(2);
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2.hashCode());
}
MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。
MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。
SqlSession
调用clearCache()
无法将数据存到二级缓存中。
POJO类实现Serializable接口。
public class User implements Serializable {
private int id;
private String username;
private String sex;
private String address;
}
在MyBatis配置文件添加如下设置:
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
由于cacheEnabled默认值是true,所以该设置可以省略。
在映射文件添加
标签,该映射文件下的所有方法都支持二级缓存。
如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过
标签的size属性修改该数量。
<cache size="2048"/>
测试二级缓存
@Test
public void testCache4() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session1 = factory.openSession();
SqlSession session2 = factory.openSession();
UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User user1 = mapper1.findById(1);
System.out.println(user1);
System.out.println(user1.hashCode());
// 让一级缓存失效
session1.commit();
System.out.println("-------------------------------------------");
User user2 = mapper2.findById(1);
System.out.println(user2);
System.out.println(user2.hashCode());
}