闲话
最近对象跟我说,她把我写进论文最后的致谢里了,没有想过这么浪漫的事也会发生在我的身上
基本要点
定义:动态SQL可以理解为,通过使用一系列标签,去动态生成我们所需要的SQL语句
1、IF标签
首先我们创建一个表,里面存放博客相关信息
DROP TABLE IF EXISTS `t_decade_blog`;
create table t_decade_blog
(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`name` VARCHAR(50) NOT NULL COMMENT '博客名称',
`author` VARCHAR(50) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` INT(20) NOT NULL COMMENT '博客浏览量'
)ENGINE = INNODB CHARACTER SET = utf8;
接着我们创建对应的Java实体类
package com.decade.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String id;
private String name;
private String author;
private Date createTime;
private int views;
}
接着我们创建接口类和对应的SQL映射文件
package com.decade.mapper;
import com.decade.entity.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
int addBlog(Blog blog);
List<Blog> queryBlogByIf(Map map);
}
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.decade.mapper.BlogMapper">
<insert id="addBlog" parameterType="Blog">
insert into t_decade_blog (id,name,author,create_time,views)
values (#{id},#{name},#{author},#{createTime},#{views})
</insert>
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
select * from t_decade_blog
where author = 'Decade0712'
<if test="views != null">
and views= #{views}
</if>
<if test="name != null">
and name = #{name}
</if>
</select>
</mapper>
另外,我们还有工具类IdUtils,用于创建随机id
package com.decade.utils;
import java.util.UUID;
public class IdUtils {
public static String getId() {
return UUID.randomUUID().toString();
}
}
和Mybatis创建sqlSession的工具类
package com.decade.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* Mybatis工具类
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 使用Mybatis第一步:获取sqlSession
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了sqlSessionFactory,我们就可以得到sqlSession实例了
// sqlSession完全包含了面向数据库执行sql命令所需要的方法,如果要开启自动提交事务,openSession中设置true即可
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
}
接着是核心配置文件Mybatis-config.xml
<?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>
<properties resource="db.properties">
<property name="username" value="decade"/>
<property name="password" value="11111"/>
</properties>
<settings>
<!-- 标准的日志工厂 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.decade.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--驱动配置,com.mysql.jdbc.driver -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--每一个mapper.xml文件都需要在核心配置文件中注册-->
<mappers>
<mapper class="com.decade.mapper.BlogMapper"/>
</mappers>
</configuration>
和数据库信息文件db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/decade_test?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
最后我们创建两个测试方法,一个插入数据,另外一个测试if标签
import com.decade.entity.Blog;
import com.decade.mapper.BlogMapper;
import com.decade.utils.IdUtils;
import com.decade.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyTest {
@Test
public void test() {
Blog blog = new Blog(IdUtils.getId(), "Mybatis系列", "Decade0712", new Date(), 2000);
Blog blog1 = new Blog(IdUtils.getId(), "Spring系列", "Decade0712", new Date(), 4000);
Blog blog2 = new Blog(IdUtils.getId(), "设计模式系列", "Decade0712", new Date(), 6000);
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
mapper.addBlog(blog);
mapper.addBlog(blog1);
mapper.addBlog(blog2);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
@Test
public void test2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("name", "Spring系列");
map.put("views", 4000);
List<Blog> blogs = mapper.queryBlogByIf(map);
blogs.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
按照预期,如果我们传入空的map,那么会查出所有的数据,否则查出符合条件的数据
运行结果如下
2、choose标签
MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
只要满足其中一个就退出判断了,后面的就不会继续判断了
otherwise等价于default,如果上述条件都不满足,就走otherwise下的条件语句
<select id="queryBlogByWhich" parameterType="map" resultType="Blog">
select * from t_decade_blog where create_time = '2022-03-28 22:30:14'
<choose>
<when test="views != null">
and views = #{views}
</when>
<when test="name != null">
and name = #{name}
</when>
<otherwise>
and author = 'Decade0712'
</otherwise>
</choose>
</select>
3、where标签
在我们使用if标签时,有可能写成这样
<select id="queryBlogInfo" parameterType="map" resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="views != null">
views= #{views}
</if>
<if test="name != null">
and name = #{name}
</if>
</select>
如果上述条件都不满足,那么查询sql就会变成
SELECT * FROM BLOG WHERE
如果只匹配第二个条件,那么sql会变成,也会查询失败
SELECT * FROM BLOG
WHERE
AND name = 'decade'
上述两种情况均会导致查询失败
为了避免这种情况发生,我们通常将where标签和if标签搭配使用,类似于
<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
select * from t_decade_blog
<where>
<if test="views != null">
and views= #{views}
</if>
<if test="name != null">
and name = #{name}
</if>
</where>
</select>
where 标签只会在满足条件的情况下才插入 “WHERE” 关键字,如果第一个条件是以 “AND” 或 “OR”为开头,where 元素也会自动将它们去除,比如这里,如果views不为空,那么where会自动将这里的and去掉,最后执行的查询语句是
SELECT * FROM BLOG
WHERE
views = 'decade'
4、set标签
set标签一般用于代替sql语句中的set关键字
<update id="updateBlog" parameterType="map">
update t_decade_blog
<set>
<if test="name != null">name=#{name},</if>
<if test="author != null">author=#{author},</if>
<if test="views != null">views=#{views}</if>
</set>
where id = #{id}
</update>
和where标签异曲同工的是,它会自动帮你去掉多余的逗号
就像上面,如果只满足第一个if,它会自动删除name后面的逗号
最后执行的sql为
update t_decade_blog set name = 'aaa' where id = 'id'
5、trim标签
可以理解为,我们可以使用trim标签来自己决定如何拼接sql
参数解释如下
prefix:在trim标签内sql语句加上前缀。
prefixOverrides:指定去除多余的前缀内容
如:prefixOverrides = "and",去除trim标签内sql语句多余的前缀"and"。
suffix:在trim标签内sql语句加上后缀。
suffixOverrides:指定去除多余的后缀内容
如:suffixOverrides=",",去除trim标签内sql语句多余的后缀","
这里,我们在sql前面加上(前缀,以)为后缀结尾,并且去掉多余的逗号
<insert id="insert" parameterType="map">
INSERT INTO t_decade_blog
<trim prefix="(" suffix=")" suffixOverrides=",">
id,name,author,create_time,views
</trim>
<trim prefix="VALUES(" suffix=")" suffixOverrides=",">
#{id},#{name}, #{author}, #{createTime},#{views}
</trim>
</insert>
如果 where 标签与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 标签的功能。比如,和 where 标签等价的自定义 trim 元素为
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
同理,set标签也可以用trim去定义
<trim prefix="SET" suffixOverrides=",">
...
</trim>
6、foreach标签
- foreach 元素的功能非常强大,你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach
- 它也允许你指定开头(open)与结尾(close)的字符串以及集合项迭代之间的分隔符(separator)
- 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素
- 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值
我们结合一个例子来学习一下,假设我们想用spring系列和mybatis系列的id去查一下博客信息
用SQL应该是
select * from t_decade_blog where id in ('76782763-48d0-4cef-b8e1-1054e181e41d','06bd6c69-e47e-4e78-9e02-5110a013e457');
现在,我们用mybatis中的foreach标签实现一下
首先我们在SQL映射文件中定义一个查询语句,这里要遍历的集合叫ids,每一次遍历得到的元素叫id,我们以(开头,用逗号做分隔符,以)结尾
<select id="queryBlogByForeach" parameterType="map" resultType="Blog">
select * from t_decade_blog where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
然后我们定义一下接口类
package com.decade.mapper;
import com.decade.entity.Blog;
import java.util.List;
import java.util.Map;
public interface BlogMapper {
List<Blog> queryBlogByForeach(Map map);
}
最后我们写一下测试方法
import com.decade.entity.Blog;
import com.decade.mapper.BlogMapper;
import com.decade.utils.IdUtils;
import com.decade.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.*;
public class MyTest {
@Test
public void test2() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String, Object> map4 = new HashMap<>();
List<String> ids = new ArrayList<>();
ids.add("06bd6c69-e47e-4e78-9e02-5110a013e457");
ids.add("76782763-48d0-4cef-b8e1-1054e181e41d");
map4.put("ids", ids);
List<Blog> blogs4 = mapper.queryBlogByForeach(map4);
blogs4.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
运行结果如下,成功查询出我们需要的数据
7、SQL片段
我们可以将重复出现的SQL部分提取出来
注意:
最好基于单表来定义sql片段,不要有太复杂的查询(例如多表查询)
不要存在where标签
<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
select * from t_decade_blog
<where>
<if test="views != null">
and views= #{views}
</if>
<if test="name != null">
and name = #{name}
</if>
</where>
</select>
上方的查询可以抽取为
<sql id="queryByIf">
<if test="views != null">
and views= #{views}
</if>
<if test="name != null">
and name = #{name}
</if>
</sql>
<select id="queryBlogByWhere" parameterType="map" resultType="Blog">
select * from t_decade_blog
<where>
<include refid="queryByIf"/>
</where>
</select>
如有错误,欢迎指正!!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136771.html