MyBatis学习笔记11、动态 SQL

动态 SQL 就是根据不同的条件生成不同的 SQL 语句。

和 JSTL 差不多,也是用各种标签

11.1、准备工作

建数据表

create table `blog` (
    `id` int(11) auto_increment comment '博客id',
    `title` varchar(100) not null comment '博客标题',
    `author` varchar(30) not null comment '博客作者',
    `create_time` datetime not null comment '创建时间',
    `views` int(11) not null comment '浏览次数',
    primary key (`id`)
) engine=InnoDB default charset=utf8

再学一点:

UUID 是通用唯一识别码(Universally Unique Identifier)。

其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。

UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的API。

UUID是有128个比特,发生碰撞的概率比一个人每年都被陨石击中的机率还低。

格式:00000000-0000-0000-0000-000000000000

java.util.UUID 类可以生成UUID

比如我们创建一个生成 ID 的类 IDUtils

public class IDUtils {
    public static String getID() {
        return UUID.randomUUID().toString().replace("-","");
    }

    @Test
    public void testGetID(){
        System.out.println(IDUtils.getID());
    }
}

运行测试部分,可以得到如下id

74fa7ee0c0a9413191d331e810e85f3d

新建 pojo 类 Blog

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

可以看出其中的 createTime 和数据表中的列名不一致,数据库中是下划线命名,而java是驼峰

mybatis有一个自动转换的功能:mapUnderscoreToCamelCase

在 mybatis-comfig.xml 中设置为 true 后下划线和驼峰就能自动映射

<settings>
	<setting name="logImpl" value="STDOUT_LOGGING"/>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

BlogMapper 接口:

public interface BlogMapper {
    int addBlog(Blog blog);
}

BlogMapper.xml :

<mapper namespace="com.dzy.dao.BlogMapper">
    <insert id="addBlog" parameterType="Blog">
        insert into blog (id, title, author, create_time, views)
        values (#{id}, #{title}, #{author}, #{createTime}, #{views})
    </insert>
</mapper>

测试:

public class TestBolgMapper {
    @Test
    public void TestAddBlog() {
        try(SqlSession session = MybatisUtils.getSqlSession()){
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog = new Blog(IDUtils.getID(), "《mybatis》", "dzy", new Date(), 9999);
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《Spring》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《SpringMVC》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);

            blog.setTitle("《SpringBoot》");
            blog.setId(IDUtils.getID());
            mapper.addBlog(blog);
            // 因为创建sqlSession时写了参数true
            // 是自动提交,所以没有session.commit()
        }
    }
}

插入成功,现在表中有四条记录。

11.2、if where标签

如果有对应的筛选,就筛选,没有就不做。

接口:

List<Blog> queryBlogIF(Map<String, Object> map);

mapper.xml

<select id="queryBlogIF" resultType="Blog">
	select * from blog where 1=1
	<if test="title != null">
		and title = #{title}
	</if>
	<if test="author != null">
		and author = #{author}
	</if>
</select>

测试1:map中什么都没有

@Test
public void testQueryBlogIF() {
	try (SqlSession session = MybatisUtils.getSqlSession()) {
		BlogMapper mapper = session.getMapper(BlogMapper.class);
		Map<String, Object> map = new HashMap<>();
		List<Blog> blogList = mapper.queryBlogIF(map);
		for (Blog b : blogList) {
			System.out.println(b);
		}
	}
}

输出所有行

测试2:在map中加入查询范围

map.put("title", "《mybatis》");
map.put("author", "dzy");

只查出标题是《mybatis》,作者是dzy的一条记录。

但是在 mapper.xml 中,有一句 1=1。这个很不好看,但是如果没有而且第一个 if 不成立,会出现 when and 的现象,sql 就不对了,而when 1=1 and xxx是对的。

where 标签解决了这个问题,where 会忽略掉 when 后面,if 标签开头的 and 或 or。我们就可以写出正常的 sql

<select id="queryBlogIF" resultType="Blog">
    select * from blog
    <where>
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
    </where>
</select>

这样,这个 sql 看起来就正常多了

11.3、choose when otherwise

向 switch-case-default 一样,多个中选一个执行

先写接口:

List<Blog> queryBlogChooes(Map<String, Object> map);

再写mapper:

<select id="queryBlogChooes" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and views > #{views}
            </otherwise>
        </choose>
    </where>
</select>

再写测试:

public void TestQueryBlogChoose() {
	try(SqlSession session = MybatisUtils.getSqlSession()) {
    	BlogMapper mapper = session.getMapper(BlogMapper.class);
    	Map<String, Object> map = new HashMap<>();
    	// map.put("title", "《mybatis》");
		// map.put("author", "dzy");
  	  	// map.put("views", 1000);
        List<Blog> blogList = mapper.queryBlogChooes(map);
    	for (Blog b : blogList) {
    		System.out.println(b);
    	}
    }
}

和 if 不同,这种方式执行且只执行一种情况,如果都不符合,执行 otherwise,如果map里什么都没有:

Opening JDBC Connection
Created connection 855700733.
==>  Preparing: select * from blog WHERE views > ? 
==> Parameters: null
<==      Total: 0
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3300f4fd]
Returned connection 855700733 to pool.

11.4、trim(where set)

where 和 set 是 trim 类型的标签,where 解决的是 when 后面有没有 and 的问题, set 解决的是 set 后面有没有逗号的问题。

比如写个update

int updateBlog(Map<String,Object> map);
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>
@Test
public void TestUpdateBlog() {
    try (SqlSession session = MybatisUtils.getSqlSession()) {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("title", "《MyBatis》");
        map.put("author", "dzydzy7");
        map.put("id", "183e05095a9643bf916bfa5e9a773664");
        mapper.updateBlog(map);
        // 自动提交
    }
}

11.5 foreach

用于对集合进行遍历,通常用在 in 后面

接口:

List<Blog> queryBloyForeach(Map<String, Object> map);

blogMapper.xml,有很多细节,很重要:

<select id="queryBloyForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <if test="titleList != null">
        and title in
        <foreach collection="titleList" item="atitle" open="(" close=")" separator=",">
            #{atitle}
        </foreach>
        </if>
    </where>
</select>

foreach标签里的属性依次是,map中集合的key,每一项的迭代名(#{}中的值),开头,结尾,分隔符。

这个sql其实是:

select * from blog WHERE title in ( ? , ? , ? ) 

测试:

@Test
public void testSelectForeach() {
    try (SqlSession session = MybatisUtils.getSqlSession()) {
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Map<String, Object> map = new HashMap<>();
        List<String> titleList = new ArrayList<>();
        titleList.add("《mybatis》");
        titleList.add("《Spring》");
        titleList.add("《SpringMVC》");
        map.put("titleList", titleList);
        List<Blog> blogList = mapper.queryBloyForeach(map);
        for (Blog b : blogList) {
            System.out.println(b);
        }
    }
}

11.6、sql 片段

sql 片段就是抽取出 sql 语句中相同的部分,以便复用

比如 if 的例子:

<select id="queryBlogIF" resultType="Blog">
	select * from blog where 1=1
	<if test="title != null">
		and title = #{title}
	</if>
	<if test="author != null">
		and author = #{author}
	</if>
</select>

可以改成:

<sql id="if-title-author">
    <where>
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </where>
</sql>

<select id="queryBlogIF" resultType="Blog">
    select * from blog
    <include refid="if-title-author"/>
</select>

尽量不用把 where 或 set 放到 sql 标签里,可能会出问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值