文章目录
1 动态SQL
元素 | 说明 |
---|---|
<if> | 判断语句,用于单条件分支判断 |
<choose> <when> <otherwise> | 相当于java中的 switch...case(break)...default ,用于多条件分支判断。 当第一个when条件为真时,则只拼接第一个SQL, 否则就向下判断第二个条件是否为真,以此类推, 当所有的when条件都不为真时,才拼装otherwise中的SQL |
<where> <trim> <set> | 辅助元素,用于处理一些 SQL 拼装、特殊字符问题 |
<foreach> | 循环语句,常用语 IN 语句等列举条件中 |
<bind> | 从 OGNL 表达式中创建一个变量,并将其绑定到上下文,常用语模糊查询的 SQL 中 |
-- SQL脚本如下
CREATE TABLE `t_user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `t_user`(`id`,`username`,`birthday`,`sex`,`address`)
values (41,'张三','2018-02-27 17:47:08','男','北京'),
(42,'小明','2018-03-02 15:09:37','女','北京金燕龙'),
(43,'小明','2018-03-04 11:34:34','女','北京金燕龙'),
(45,'李四','2018-03-04 12:04:06','男','北京金燕龙'),
(46,'王五','2018-03-07 17:37:26','男','北京'),
(47,'王六','2018-03-07 17:37:26','男','洛阳'),
(48,'赵六','2018-03-08 11:44:00','女','北京修正');
-
依赖
<properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.22</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
-
配置
-
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE. #log4j.rootCategory=INFO, CONSOLE debug info warn error fatal log4j.rootCategory=debug, CONSOLE, LOGFILE # Set the enterprise logger category to FATAL and its only appender to CONSOLE. log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n # LOGFILE is set to be a File appender using a PatternLayout. log4j.appender.LOGFILE=org.apache.log4j.FileAppender log4j.appender.LOGFILE.File=d:\axis.log log4j.appender.LOGFILE.Append=true log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
-
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"> <!-- mybatis的主配置文件 --> <configuration> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(连接池) --> <dataSource type="POOLED"> <!-- 配置连接数据库的4个基本信息 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.181.160:3306/test?serverTimezone=CTT"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 --> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
-
-
编码
package cn.pojo; import lombok.Data; @Data public class User { /** * 加个 user 前缀,特意与数据库中的列不一样,测试 ResultMap * <p> * 不同的数据类型 ResultMap 映射结果如下 * private Date userBirthday; ==> userBirthday=Tue Feb 27 17:47:08 CST 2018 * private String userBirthday; ==> userBirthday=2018-02-27 17:47:08 */ private Integer userId; private String userName; // private Date userBirthday; private String userBirthday; private String userSex; private String userAddress; }
package cn.pojo; import lombok.Data; @Data public class UserDto { /** * User的部分属性 * 加入 t_user 表中有50个字段,那么User类就有与之对应的50个属性 * 而大部分查询时只需要查其中的20个属性,因此可以把该20个属性单独定义成一个dto对象 */ private String userName; private String userSex; private String userAddress; }
package cn.mapper; import cn.pojo.User; import cn.pojo.UserDto; import java.util.List; public interface UserMapper { // 查询所有用户,主要用于环境测试 List<User> findAll(); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.mapper.UserMapper"> <select id="findAll" resultMap="userMap"> select * from t_user </select> <!--在此配置userMap,后面的动态SQL均引用此处的userMap,后面不在重复写--> <resultMap id="userMap" type="cn.pojo.User"> <id property="userId" column="id"/> <result property="userName" column="username"/> <result property="userBirthday" column="birthday"/> <result property="userAddress" column="address"/> <result property="userSex" column="sex"/> </resultMap> </mapper>
-
测试
/** * 动态SQL测试类 */ public class TestDynamicSql { private InputStream inputStream = null; private SqlSession session = null; private UserMapper userMapper = null; @Before public void init() throws IOException { // 1.读取配置文件 inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 2.创建SqlSessionFactory工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); // 3.使用工厂生产SqlSession对象 session = factory.openSession(); // 4.使用SqlSession创建Dao接口的代理对象 userMapper = session.getMapper(UserMapper.class); // 5.执行对象方法,即下面的各个测例 } @After public void destroy() throws IOException { // 6.释放资源 session.close(); inputStream.close(); } /** * findAll环境测试 */ @Test public void testEnv() { List<User> list = userMapper.findAll(); for (User user : list) { System.out.println(user); } /** * 结果 * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京) * User(userId=42, userName=小明, userBirthday=2018-03-02 15:09:37, userSex=女, userAddress=北京金燕龙) * User(userId=43, userName=小明, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=北京金燕龙) * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙) * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正) */ } }
-
项目结构图(源码见文末连接)
1.1 if
-
接口
/** * 需求:如果名字不为空,则根据名字查找用户 * 如果地址也不为空,则根据名字和地址查找用户 * 如果两者都为空,则查找全部用户 * <p> * 用于多条件查询,比如书城买书时根据书名、作者等信息进行综合查找 */ List<User> findUserByUsernameAndAddress(User user);
-
实现
<select id="findUserByUsernameAndAddress" parameterType="cn.pojo.User" resultMap="userMap"> SELECT * FROM t_user WHERE 1=1 <if test="userName != null and userName!='' "> and username like concat('%',#{userName},'%') </if> <if test="userAddress != null and userAddress!='' "> and address like concat('%',#{userAddress},'%') </if> </select>
-
测试
/** * if标签测试 * <p> * 需求:如果名字不为空,则根据名字查找用户 * 如果地址也不为空,则根据名字和地址查找用户 * 如果两者都为空,则查找全部用户 * <p> * 用于多条件查询,比如书城买书时根据书名、作者等信息进行综合查找 * <p> * 查找名字中含有"王"字,且住址是"北京"的人(可以注释条件观察控制台打印的sql语句) */ @Test public void test1() { User user = new User(); user.setUserName("王"); user.setUserAddress("北京"); List<User> users = userMapper.findUserByUsernameAndAddress(user); users.forEach(u -> System.out.println(u)); /** * 结果 * (1) 条件只有 user.setUserName("王"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) * * (2) 条件只有 user.setUserAddress("北京"); * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京) * User(userId=42, userName=小明, userBirthday=2018-03-02 15:09:37, userSex=女, userAddress=北京金燕龙) * User(userId=43, userName=小明, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=北京金燕龙) * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙) * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正) * * (3) 条件有 user.setUserName("王"); 和 user.setUserAddress("北京"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * * (4) 无条件(即上面两个set赋值均注释掉) * 结果为所有用户、同上面 testEnv 测试结果,因此此处略 */ }
1.2 choose、when、otherwise
-
接口
/** * 需求:当用户名不为空时,则只根据用户名进行筛选 (即优先级用户名大于地址) * 当用户名为空,而住址不为空时,则只根据住址进行筛选 * 当用户名和住址都为空时,则筛选所有男性用户 */ List<User> findUserByUsernameOrAddress(User user);
-
实现
<select id="findUserByUsernameOrAddress" parameterType="cn.pojo.User" resultMap="userMap"> select * from t_user where 1=1 <choose> <when test="userName != null and userName!=''"> and username like concat('%',#{userName},'%') </when> <when test="userAddress != null and userAddress!=''"> and address = #{userAddress} </when> <otherwise> and sex="男" </otherwise> </choose> </select>
-
测试
/** * choose、when、otherwise标签测试 * <p> * 需求:当用户名不为空时,则只根据用户名进行筛选 (即优先级用户名大于地址) * 当用户名为空,而住址不为空时,则只根据住址进行筛选 * 当用户名和住址都为空时,则筛选所有男性用户 */ @Test public void test2() { User user = new User(); // user.setUserName("王"); // user.setUserAddress("北京"); List<User> users = userMapper.findUserByUsernameOrAddress(user); users.forEach(u -> System.out.println(u)); /** * 结果 * (1) 条件只有 user.setUserName("王"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) * * (2) 条件只有 user.setUserAddress("北京"); * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京) * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * * (3) 条件有 user.setUserName("王"); 和 user.setUserAddress("北京"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) * * (4) 无条件(即上面两个set赋值均注释掉) * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京) * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙) * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) */ }
1.3 where、trim、set
-
接口
/** * 用where标签替换 "select * from user where 1=1 "中的 " 1=1 " , * 在前面的sql中若是没有 "1=1",导致where后直接拼接and,造成sql语法错误 */ List<User> findUserByWhere(User user); /** * 除了使用where外,还可以使用trim来制定需要的功能。 * trim的功能是去除一些特殊的字符串。 */ List<User> findUserByTrim(User user); /** * 在更新的时候,大多数情况下都是更新的某一个或几个字段, * 若是将一条数据的所有属性都更新一遍,那么执行效率非常差, * 因此可以用mybatis中的set关键字解决此问题。 */ List<User> updateUserBySet(User user);
-
实现
<select id="findUserByWhere" parameterType="cn.pojo.User" resultMap="userMap"> SELECT * FROM t_user <where> <if test="userName != null and userName!='' "> and username like concat('%',#{userName},'%') </if> </where> </select> <!-- trim元素的作用是去除一些特殊的字符串 prefix:代表前缀。这里使用where来连接后面的sql片段。 prefixOverrides:代表要去除的特殊字符串。这里定义了要去除sql中的and。 --> <select id="findUserByTrim" parameterType="cn.pojo.User" resultMap="userMap"> select * from t_user <trim prefix="where" prefixOverrides="and"> <if test="userName != null and userName!='' "> and username like concat('%',#{userName},'%') </if> </trim> </select> <!-- set元素主要用于更新操作, 其主要作用是在动态包含的sql语句中输出一个set关键字, 并将sql语句的最后一个多余的逗号删除。 --> <select id="updateUserBySet" parameterType="cn.pojo.User"> update t_user <set> <if test="userName != null and userName!='' "> username = #{userName} , </if> <if test="userAddress != null and userAddress!=''"> address = #{userAddress} , </if> </set> where id=#{userId} </select>
-
测试
/** * where标签测试 * <p> * 用where标签替换 "select * from user where 1=1 "中的 " 1=1 " , * 在前面的sql中若是没有 "1=1",导致where后直接拼接and,造成sql语法错误 */ @Test public void test3() { User user = new User(); user.setUserName("王"); List<User> users = userMapper.findUserByWhere(user); users.forEach(u -> System.out.println(u)); /** * 无条件结果 * 即查询所有用户,结果同testEnv,略 * * 含条件 user.setUserName("王"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) */ } /** * trim标签测试 * <p> * 除了使用where外,还可以使用trim来制定需要的功能。 * trim的功能是去除一些特殊的字符串。 */ @Test public void test4() { User user = new User(); user.setUserName("王"); List<User> users = userMapper.findUserByTrim(user); users.forEach(u -> System.out.println(u)); /** * 无条件结果 * 即查询所有用户,结果同testEnv,略 * * 含条件 user.setUserName("王"); * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) */ } /** * set标签测试 * <p> * 在更新的时候,大多数情况下都是更新的某一个或几个字段, * 若是将一条数据的所有属性都更新一遍,那么执行效率非常差, * 因此可以用mybatis中的set关键字解决此问题。 */ @Test public void test5() { User user = new User(); user.setUserId(43); user.setUserName("刘小邦"); user.setUserAddress("咸阳"); userMapper.updateUserBySet(user); }
1.4 foreach
-
接口
/** * mybatis中提供了一种用于数组和集合循环遍历的方式。 * foreach常和in连用。 */ List<User> findUsersByIds(List<Integer> ids);
-
实现
<!-- item指定的即为循环元素的名称。如 for(String str: books) 中的str--> <select id="findUsersByIds" parameterType="List" resultMap="userMap"> select * from t_user where id in <foreach collection="list" item="currentId" open="(" separator="," close=")"> #{currentId} </foreach> </select>
-
测试
/** * foreach标签测试 * <p> * mybatis中提供了一种用于数组和集合循环遍历的方式。 * foreach常和in连用。 */ @Test public void test6() { List<Integer> ids = Arrays.asList(41, 43, 46, 48); List<User> users = userMapper.findUsersByIds(ids); users.forEach(u -> System.out.println(u)); /** * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京) * User(userId=43, userName=刘小邦, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=咸阳) * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京) * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正) */ }
1.5 bind
-
接口
/** * 在进行sql模糊查询的时候,如果使用"${}",则无法防止sql注入问题; * 如果使用concat()函数进行连接,则只针对mysql数据库有效; * 若是使用oracle数据库,则需要使用"||" * 因此,mybatis提供了bind标签来解决这个问题 */ List<User> findUserByBind(User user);
-
实现
<select id="findUserByBind" parameterType="cn.pojo.User" resultMap="userMap"> <bind name="bindUserName" value="'%' + userName + '%'"/> select * from t_user where username like #{bindUserName} </select>
-
测试
/** * bind 标签测试 * <p> * 在进行sql模糊查询的时候,如果使用"${}",则无法防止sql注入问题; * 如果使用concat()函数进行连接,则只针对mysql数据库有效; * 若是使用oracle数据库,则需要使用"||" * 因此,mybatis提供了bind标签来解决这个问题 */ @Test public void test7() { User user = new User(); user.setUserName("六"); List<User> users = userMapper.findUserByBind(user); users.forEach(u -> System.out.println(u)); /** * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳) * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正) */ }
1.6 sql
-
接口
/** * 可将重复的sql提取出来,使用时include标签引入, * 这样不仅可以使代码简洁,而且还能达到sql重用的目的。 * <p> * 特别是一张表中字段非常多时,假设50个,好多查询的sql语句都只需要其中20个字段, * 那么此时sql标签就非常有用,把这经常用到的20个字段用sql标签定义用的时候引入即可 */ List<UserDto> findUserBySql();
-
实现
<!--用include标签(单标签)进行引入,后面续写sql即可(eg. from user)--> <select id="findUserBySql" resultMap="userDtoMap"> select <include refid="needColumn"/> from t_user </select> <!--定义查询时所需要的的列--> <sql id="needColumn"> username,sex,address </sql> <resultMap id="userDtoMap" type="cn.pojo.UserDto"> <result property="userName" column="username"/> <result property="userAddress" column="address"/> <result property="userSex" column="sex"/> </resultMap>
-
测试
/** * sql标签测试 * <p> * 可将重复的sql提取出来,使用时include标签引入, * 这样不仅可以使代码简洁,而且还能达到sql重用的目的。 * <p> * 特别是一张表中字段非常多时,假设50个,好多查询的sql语句都只需要其中20个字段, * 那么此时sql标签就非常有用,把这经常用到的20个字段用sql标签定义用的时候引入即可 */ @Test public void test8() { List<UserDto> users = userMapper.findUserBySql(); users.forEach(u -> System.out.println(u)); /** * UserDto(userName=张三, userSex=男, userAddress=北京) * UserDto(userName=小明, userSex=女, userAddress=北京金燕龙) * UserDto(userName=刘小邦, userSex=女, userAddress=咸阳) * UserDto(userName=李四, userSex=男, userAddress=北京金燕龙) * UserDto(userName=王五, userSex=男, userAddress=北京) * UserDto(userName=王六, userSex=男, userAddress=洛阳) * UserDto(userName=赵六, userSex=女, userAddress=北京修正) */ }
- 源码链接:blogs-mybatis