1 概述
本文次要讲述了如何应用 MyBatis
中的映射器以及动静 SQL
的配置。
2 MyBatis
配置文件概览
MyBatis
配置文件次要属性如下:
<settings>
:相干设置,键值对模式<typeAliases>
:类型别名<typeHandlers>
:类型处理器<objectFactory>
:对象工厂<plugins>
:插件,蕴含若干个<plugin>
<environments>
:环境配置,蕴含若干个<environment>
,在<environment>
中能够指定事务管理器<transactionManager>
以及数据源<dataSource>
<databaseIdProvider>
:数据库厂商标识<mappers>
:映射器,蕴含若干个<mapper>
留神程序不能颠倒,否则启动时会产生异样。
3 筹备步骤
因为本文大部分的代码都只给出了要害的语句而没有残缺的工程,因而如果想要实现一遍请 clone
此处的代码(Kotlin
请 clone
此处),并:
- 利用
resources/sql
下的脚本文件创建数据库以及数据表,并插入相应数据 - 批改
MyBatis
、Spring
、dhcp2
等依赖为最新版本并批改MySQL
驱动为对应版本 - 批改
applicationContext.xml
文件中的数据库用户名,明码以及数据库URL
,可能须要批改驱动 - 开启数据库服务并进行测试,运行
MainTest
中的测试方法即可,失常来说会呈现如下后果:
4 映射器概述
MyBatis
的映射器由一个接口加上 XML
映射文件组成,是最简单的组件,映射文件罕用元素如下:
<select>
:查问语句<insert>
/<update>
/<delete>
:插入
/更新
/删除
语句,返回操作所影响的行数,比方插入了两行,操作胜利了影响的行数则为两行,返回整数 2<sql>
:自定义的SQL
<resultMap>
:提供映射规定
上面先来看一下最罕用的<select>
。
4.1 <select>
示例(在 mapper/UserDao.xml
间接增加即可):
<select id="selectById" parameterType="Integer" resultType="pers.init.entity.User">
select * from user where id = #{id}
</select>
其中 id
是惟一标识符,承受一个 Integer
,返回com.pojo.User
对象,后果集主动映射到 com.pojo.User
中。
罕用属性如下:
id
:<select>
语句的全局惟一标识符paramterType
:示意传入SQL
语句的参数类型的全限定名或别名,可选,能主动推断resultType
:执行SQL
后返回的类型resultMap
:与resultType
相似,resultType
默认一一对应映射,比方表字段名为id
,则映射到实体类的id
中,而resultMap
须要手动定义映射关系,这样就能够把表字段中的id
映射到实体类的id1
,或id2
,或id3
,resultType
与resultMap
两者须要指定一个,不能同时存在flushCache
:设置调用SQL
后是否要求MyBatis
清空之前查问的本地缓存以及二级缓存,默认false
useCache
:启动二级缓存,默认true
timeout
:超时参数,单位秒fetchSize
:获取记录的总条数设定statementType
:应用哪个JDBC
的Statement
,取值能够为STATEMENT
/PREPARED
/CALLABLE
,别离示意Statement
/PreparedStatement
/CallableStatement
resultSetType
:针对JDBC
的ResultSet
,可设置为FORWARD_ONLY
/SCROLL_SENSITIVE
/SCROLL_INSENSITIVE
,别离示意只容许向前拜访
/双向滚动,不及时更新
/双向滚动,及时更新
并批改 UserDao
,增加一个selectById
办法:
User selectById(Integer id);
能够间接测试了:
@Test
public void selectById()
{System.out.println(dao.selectById(1));
}
上面来看一下如何传递多个参数。
4.2 传递参数
有了最根本的 select
后,传递 id
这种繁多参数很容易,然而理论状况中很多时候须要传递多个参数,MyBatis
中传递多个参数有两种形式:
- 通过
Map
传递 - 通过
JavaBean
传递
4.2.1 Map
能够应用 Map
传递多个参数,示例 <select>
如下:
<select id="selectByMap" resultType="pers.init.entity.User" parameterType="map">
select * from user where name like concat('%', #{name}, '%') and age = #{age}
</select>
参数名 name
以及 age
是Map
的键。
接着在 UserDao
下增加:
User selectByMap(Map<String,String> map);
而后在主类中应用 Map
增加键值对:
@Test
public void selectByMap()
{Map<String,String> map = new HashMap<>();
map.put("name","111");
map.put("age","33");
System.out.println(dao.selectByMap(map));
}
这样就能传递多个参数进行查问了。
4.1.2 应用JavaBean
传递多个参数的另一种办法是利用 JavaBean
传递,创立一个 POJO
类:
@Getter
@Setter
@Builder
@ToString
public class UserPOJO {
private String name;
private Integer age;
}
批改 UserDao
接口办法:
public User selectByPOJO(UserPOJO user)
接着批改映射文件,实际上批改 parameterType
即可:
<select id="selectByPOJO" resultType="pers.init.entity.User" parameterType="pers.init.pojo.UserPOJO">
select * from user where name like concat('%', #{name}, '%') and age = #{age}
</select>
留神拜访传递的参数时间接应用 POJO
类的属性名即可,毋庸加上相似 UserPOJO.
的前缀。
最初进行测试:
@Test
public void selectByPOJO()
{UserPOJO pojo = UserPOJO.builder().age(33).name("111").build();
System.out.println(dao.selectByPOJO(pojo));
}
4.2 <insert>
<insert>
用于插入,大部分属性与 <select>
雷同,上面是几个特有属性:
keyProperty
:将插入操作的返回值赋给POJO
类的某个属性keyColumn
:用于设置主键列的地位,当表中第 1 列不是主键时须要设置该参数,联结主键能够应用逗号分隔useGeneratedKeys
:应用JDBC
的getGeneratedKeys
获取数据库外部产生的主键,默认false
比方典型的主键回填 <insert>
如下:
<insert id="insertUser1" parameterType="pers.init.entity.User" keyProperty="id" useGeneratedKeys="true">
insert into user(name, age) values (#{name}, #{id})
</insert>
这样就会利用数据库生成的自增主键回填到 User
的id
属性中,UserDao
接口如下:
int insertUser1(User user);
一般来说插入操作返回一个整数,示意操作影响的行数,因而能够设置返回值为int
,测试如下:
@Test
public void insertUser1()
{User user = User.builder().age((short) 88).name("test1").build();
System.out.println(dao.insertUser1(user));
System.out.println(user.getId());
}
另外如果不反对自增主键,能够应用 selectKey
自定义生成主键,比方:
<insert id="insertUser2" parameterType="pers.init.entity.User">
<selectKey keyProperty="id" resultType="integer" order="BEFORE">
select if(max(id) is null,1,max(id)+1) as newId from user
</selectKey>
insert into user(id,name,age) values(#{id},#{name},#{age})
</insert>
<selectKey>
中的 keyProperty
指定了新主键 newId
返回给 pers.pojo.User
的id
属性,order
设置执行程序,BEFORE
/AFTER
示意执行 <selectKey>
之后 / 之前再执行插入语句。
测试:
@Test
public void insertUser2()
{User user = User.builder().age((short) 10).name("test2").build();
System.out.println(dao.insertUser2(user));
System.out.println(user.getId());
}
4.3 <update>
/<delete>
返回一个整数,属性与 <insert>
/<select>
相似,简略示例如下:
<update id="updateUser" parameterType="pers.init.entity.User">
update user set name=#{name}, age=#{age} where id = #{id}
</update>
<delete id="deleteUser" parameterType="Integer">
delete from user where id = #{id}
</delete>
同理 update/delete
返回一个整数,示意操作影响的行数,因而设置 UserDao
接口如下:
int updateUser(User user);
int deleteUser(Integer id);
测试:
@Test
public void updateUser()
{User user = User.builder().id(3).name("3333333").age((short)11).build();
selectAll();
System.out.println(dao.updateUser(user));
selectAll();}
@Test
public void deleteUser()
{selectAll();
System.out.println(dao.deleteUser(3));
selectAll();}
4.4 <sql>
用于定义 SQL
的一部分,以不便前面的 SQL
语句援用,比方:
<sql id="column">
id,name,age
</sql>
<select id="selectBySqlColumn" resultType="pers.init.entity.User">
select <include refid="column"/> from user
</select>
UserDao
接口:
List<User> selectBySqlColumn();
测试:
@Test
public void selectBySqlColumn()
{System.out.println(dao.selectBySqlColumn());
}
5 <resultMap>
下面进步过,<resultMap>
比 <resultType>
要弱小,然而须要手动定义映射关系,一个常见的 <resultMap>
如下:
<resultMap type="package1.package2.package3.POJO" id="resultMapId">
<constrcutor> <!-- 实例化时将后果注入到构造方法中 -->
<idArg /> <!--ID 参数 -->
<arg /> <!-- 一般参数 -->
</constrcutor>
<id /> <!-- 示意哪个列是主键 -->
<result /> <!-- 注入到字段 /JavaBean 属性的一般后果 -->
<association property=""> <!-- 一对一关联 -->
<collection property=""> <!-- 一对多关联 -->
<discriminator javaType=""> <!-- 应用后果值决定哪个后果映射 -->
<case value=""> <!-- 基于某些值的后果映射 -->
</discriminator>
</resultMap>
5.1 应用Map
查问 SQL
的后果能够应用 Map
/POJO
存储,应用 Map
存储不须要手动编写<resultMap>
,默认表属性名是键值对的键:
<select id="selectReturnMap" resultType="Map">
select * from user
</select>
可用 List<Map>
来接管返回后果,一条记录映射到一个 Map
对象,Map
中的 key
是select
的字段名。
示例的 UserDao
办法如下:
List<Map<String,Object>> selectReturnMap();
其中 Map
类型为Map<String,Object>
,测试方法如下:
@Test
public void selectReturnMap()
{dao.selectReturnMap().forEach(System.out::println);
}
5.2 应用POJO
如果应用 POJO
存储返回的对象时,须要先定义一个 POJO
类,能够在下面的 UserPOJO
根底上加上一个 id
属性:
@Getter
@Setter
@Builder
@ToString
public class UserPOJO {
private Integer id;
private String name;
private Integer age;
}
接着编写映射文件:
<resultMap id="testPOJO" type="pers.init.pojo.UserPOJO">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
</resultMap>
其中 property
指定 POJO
的属性,column
是表字段名,最初配合 <select>
应用,指定 resultMap
为对应id
:
<select id="selectReturnPOJO" resultMap="testPOJO">
select * from user
</select>
返回后果能够用 List<UserPOJO>
接管:
List<UserPOJO> selectReturnPOJO();
测试方法:
@Test
public void selectReturnPOJO()
{dao.selectReturnPOJO().forEach(System.out::println);
}
6 级联查问
级联查问就是利用主键与外键的关系进行组合查问,比方表 A
的一个外键援用了表 B
的一个主键,查问 A
时,通过 A
的外键将 B
的相干记录返回,这就是级联查问。常见的级联查问有三种:
- 一对一
- 一对多
- 多对多
MyBatis
反对一对一以及一对多级联,没有对多对多级联提供反对,然而能够用多个一对多级联实现多对多级联。上面别离来看一下。
6.1 一对一
一对一级联查问是最常见的级联查问,能够通过 <resultMap>
中的 <association>
进行配置,通常应用的属性如下:
property
:映射到实体类的对象属性column
:指定表中对应的字段javaType
:指定映射到实体对象属性的类型select
:指定引入嵌套查问的子SQL
语句,用于关联映射中的嵌套查问
上面通过一个例子进行阐明,例子分五步:
- 创立数据表
- 创立实体类
- 编写映射文件
- 批改长久层接口
- 增加测试方法
6.1.1 数据表
为了不便新增表以及数据都写在一起:
use test;
drop table if exists idcard;
drop table if exists person;
create table idcard(id int(10) primary key auto_increment,
code char(18) collate utf8mb4_unicode_ci default null
);
create table person(id int(10) primary key,
name varchar(20) collate utf8mb4_unicode_ci default null,
age smallint default null,
idcard_id int(10) default null,
key idcard_id(idcard_id),
constraint idcard_id foreign key (idcard_id) references idcard(id)
);
insert into idcard(`code`) values('123456789123456789');
insert into person(`id`,`name`,`age`,`idcard_id`) values (1,'111',22,1);
6.1.2 实体类
@Data
public class IdCard {
private Integer id;
private String code;
}
@Data
public class Person {
private Integer id;
private String name;
private Integer age;
private IdCard card;
}
另外还须要创立一个映射后果的 POJO
类:
@Data
public class PersonPOJO {
private Integer id;
private String name;
private Short age;
private String code;
}
6.1.3 映射文件
映射文件分为两个:
IdCardMapper.xml
PersonMapper.xml
首先是 IdCardMapper.xml
,加上一个<select>
即可,留神 namespace
的地位填写正确,对应 dao
的地位。
<?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="pers.oneToOne.dao.IdCardDao">
<select id="selectCodeById" parameterType="Integer" resultType="pers.oneToOne.entity.IdCard">
select * from idcard where id = #{id}
</select>
</mapper>
其次是PersonMapper.xml
:
<?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="pers.oneToOne.dao.PersonDao">
<resultMap id="personMap1" type="pers.oneToOne.entity.Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="card" column="idcard_id" javaType="pers.oneToOne.entity.IdCard"
select="pers.oneToOne.dao.IdCardDao.selectCodeById"/>
</resultMap>
<select id="selectPersonById1" parameterType="Integer" resultMap="personMap1">
select *
from person
where id = #{id}
</select>
<resultMap id="personMap2" type="pers.oneToOne.entity.Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="card" javaType="pers.oneToOne.entity.IdCard">
<id property="id" column="idcard_id"/>
<result property="code" column="code"/>
</association>
</resultMap>
<select id="selectPersonById2" parameterType="Integer" resultMap="personMap2">
select p.*, ic.code
from person p,
idcard ic
where p.idcard_id = ic.id
and p.id = #{id}
</select>
<select id="selectPersonById3" parameterType="Integer" resultType="pers.oneToOne.pojo.PersonPOJO">
select p.*, ic.code
from person p,
idcard ic
where p.idcard_id = ic.id
and p.id = #{id}
</select>
</mapper>
首先第一个 <resultMap>
先指定 id
等属性,接着是<association>
:
property
是实体类属性,留神类型为IdCard
column
是表字段名,类型为int(10)
javaType
是通过前面的select
返回的类型,能够了解成是property
的类型,也就是IdCard
select
指定嵌套查问应用的SQL
,对应于IdCardDao.xml
中的selectCodeById
接着在一个 <select>
中的 resultMap
指定该 map
的id
即可。应用这种办法执行的是两次SQL
:
- 一次是
select * from person where id=?
- 一次是
select * from idcard where id=?
最初再把后果整起起来,开启调试能够发现实际上也是执行了两条SQL
:
而第二个 <resultMap>
中,在 <association>
外面没有了 select
属性,间接将后果映射到 SelectPersonById
中,这是执行一条 SQL
语句的后果:
select p.*,ic.code from person p,idcard ic where p.idcard_id = ic.id and p.id=#{id}
理论查问如下:
如果须要重要能够将其配置成<resultMap>
,比方:
<association property="card" resultMap="resultMap" />
<resultMap id="resultMap">
<id property="id" column="idcard_id"/>
<result property="code" column="code"/>
</resultMap>
而最初一个 <select>
是进行连贯查问,无需额定的<resultMap>
,理论执行状况如下:
6.1.4 Dao
接口
这个比较简单:
public interface PersonDao {Person selectPersonById1(Integer id);
Person selectPersonById2(Integer id);
PersonPOJO selectPersonById3(Integer id);
}
6.1.5 测试
@Test
public void selectPersonById()
{System.out.println(dao.selectPersonById1(1));
System.out.println(dao.selectPersonById2(1));
System.out.println(dao.selectPersonById3(1));
}
留神在测试之前,须要批改配置文件mybatis-config.xml
:
<configuration>
<settings>
<!-- 提早加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 按需加载 -->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- 调试开关,打印执行的 SQL-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
</settings>
<mappers>
<!--initMapper-->
<mapper resource="mapper/init/UserMapper.xml" />
<mapper resource="mapper/init/TestMapper.xml" />
<!-- 一对一级联 Mapper-->
<mapper resource="mapper/oneToOne/PersonMapper.xml" />
<mapper resource="mapper/oneToOne/IdCardMapper.xml" />
</mappers>
</configuration>
前两个 <setting>
示意开启提早加载以及按需加载,前面一个是设置调试开关,最初在上面的 <mappers>
加上 <mapper>
对应的 xml
的地位。
要留神的一个是 <settings>
须要写在 <mappers>
的后面。
另外因为 Dao
接口没有加上 @Mapper
注解,因而须要在 applicationContext.xml
中手动加上 Dao
地位:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="pers.oneToOne.dao"/>
<property name="sqlSessionFactoryBeanName" value="sql.SessionFactory"/>
</bean>
测试后果:
6.2 一对多
一对多的级联查问与一对一解决有相似之处,次要是映射文件中的 <collection>
配置,例子也是和下面一样分五步。
6.2.1 数据表
须要两张表:
user
order
user
能够沿用后面的 user
表,而 order
表如下:
use test;
drop table if exists orders;
create table orders(id int(10) primary key auto_increment,
ordersn varchar(10) collate utf8mb4_unicode_ci default null,
user_id int(10) default null,
key user_id(user_id),
constraint user_id foreign key (user_id) references user(id)
);
insert into orders(`ordersn`,`user_id`) values ('testorder1',1),('testorder2',1),('testorder3',1);
6.2.2 实体类
增加实体类Orders
:
@Data
public class Orders {
private Integer id;
private String ordersn;
}
同时创立一个带 Orders
的User
:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserWithOrders {
private Integer id;
private String name;
private Short age;
private List<Orders> ordersList;
}
6.2.3 映射文件
两个:
OrdersMapper.xml
UserWithOrdersMapper.xml
首先是OrdersMapper.xml
,只有一个简略的<select>
:
<?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="pers.oneToMany.dao.OrdersDao">
<select id="selectOrdersById" parameterType="Integer" resultType="pers.oneToMany.entity.Orders">
select * from orders where user_id=#{id}
</select>
</mapper>
接着是UserWithOrdersMapper.xml
:
<?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="pers.oneToMany.dao.UserWithOrdersDao">
<resultMap id="userAndOrder1" type="pers.oneToMany.entity.UserWithOrders">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<collection property="ordersList" ofType="pers.oneToMany.entity.Orders" column="id" select="pers.oneToMany.dao.OrdersDao.selectOrdersById"/>
</resultMap>
<select id="selectUserOrders1" parameterType="Integer" resultMap="userAndOrder1">
select * from user where id=#{id}
</select>
<resultMap id="userAndOrder2" type="pers.oneToMany.entity.UserWithOrders">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<collection property="ordersList" ofType="pers.oneToMany.entity.Orders">
<id property="id" column="id" />
<result property="ordersn" column="ordersn"/>
</collection>
</resultMap>
<select id="selectUserOrders2" parameterType="Integer" resultMap="userAndOrder2">
select u.*,o.id,o.ordersn from user u,orders o where u.id = o.user_id and u.id = #{id}
</select>
<select id="selectUserOrders3" parameterType="Integer" resultType="pers.oneToMany.pojo.UserOrdersPOJO">
select u.*,o.id,o.ordersn from user u,orders o where u.id = o.user_id and u.id = #{id}
</select>
</mapper>
相比起一对一的级联,重点扭转的就是其中的<collection>
,重要属性如下:
property
:指定实体类的属性字段ofType
:指定汇合中的类型column
:将哪些值传递给select
中的办法select
:嵌套查问的语句
第二个 <collection>
相似,将查问的后果间接映射到 Orders
的属性下面。最初一种是间接应用连贯查问。
6.2.4 Dao
接口
public interface OrdersDao {List<Orders> selectOrdersById(Integer id);
}
public interface UserWithOrdersDao {UserWithOrders selectUserOrders1(Integer id);
UserWithOrders selectUserOrders2(Integer id);
List<UserOrdersPOJO> selectUserOrders3(Integer id);
}
6.2.5 测试
@Test
public void selectUserOrders()
{System.out.println(dao.selectUserOrders1(1));
System.out.println(dao.selectUserOrders2(1));
System.out.println(dao.selectUserOrders3(1));
}
6.3 多对多
MyBaits
其实不反对多对多级联,然而能够通过多个一对多级联实现,比方一个订单对应多个商品,一个商品对应多个订单,这样两者就是多对多级联关系,这样应用一个两头表,就能够转换为两个一对多关系。
上面同样通过五个步骤实现多对多级联。
6.3.1 数据表
须要订单表、商品表以及一个两头表,因为订单表 Orders
之前已创立,这里只须要创立两个表:
use test;
create table product(id int(10) primary key auto_increment,
name varchar(10) collate utf8mb4_unicode_ci default null,
price double default null
);
create table orders_detail(id int(10) primary key auto_increment,
orders_id int(10) default null,
product_id int(10) default null,
key orders_id(orders_id),
key product_id(product_id),
constraint orders_id foreign key (orders_id) references orders(id),
constraint product_id foreign key (product_id) references product(id)
);
insert into product(`name`,`price`) values('product1',1.1),('product2',2.2),('product3',3.3);
insert into orders_detail(`orders_id`,`product_id`) values(1,1),(1,2),(1,3),(2,1),(2,3);
6.3.2 实体类
订单类能够沿用之前的,只须要两个实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
private Integer id;
private String name;
private Double price;
private List<Orders> orders;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrdersWithProduct {
private Integer id;
private String ordersn;
private List<Product> products;
}
6.3.3 映射文件
<?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="pers.manyToMany.dao.OrdersWithProductDao">
<resultMap id="ordersAndProduct" type="pers.manyToMany.entity.OrdersWithProduct">
<id property="id" column="id"/>
<result property="ordersn" column="ordersn"/>
<collection property="products" ofType="pers.manyToMany.entity.Product">
<id property="id" column="pid"/>
<result property="name" column="name"/>
<result property="price" column="price"/>
</collection>
</resultMap>
<select id="selectOrdersAndProduct" resultMap="ordersAndProduct">
select o.*,p.id as pid ,p.name,p.price from orders o,orders_detail od, product p where o.id = od.orders_id and od.product_id = p.id
</select>
</mapper>
这里的多对多级联本质上是通过每次指定不同的 OrdersId
去查问对应的 Product
实现的,也就是分成了屡次的一对多级联。
6.3.4 Dao
接口
public interface OrdersWithProductDao {List<OrdersWithProduct> selectOrdersAndProduct();
}
6.3.5 测试
@Test
public void test()
{ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
OrdersWithProductDao dao = context.getBean(OrdersWithProductDao.class);
System.out.println(dao.selectOrdersAndProduct());
}
7 动静SQL
最初来看一下动静 SQL
,动静SQL
能够防止手动拼接 SQL
语句,比方在某些条件成立的状况下增加 and xxx=xxxx
之类的操作。先来看一下最罕用的<if>
。
7.1 <if>
<if>
相似 Java
中的 if
语句,最简略的例子如下:
<select id="selectByIf" parameterType="Integer" resultType="pers.init.entity.User">
select * from user where 1=1
<if test="name != null and name !=''">
and name like concat('%',#{name},'%')
</if>
<if test="age != null and age>0">
and age = #{age}
</if>
</select>
也就是说当 test
中的条件成立时,便增加 and xxx
语句。留神 test
这个属性是 <if>
必须的,不能省略。
(注这里用到了一个要害的 1=1
,仅作阐明应用,理论开发请勿应用1=1
进行拼接)
Dao
接口:
List<User> selectByIf(User user);
测试:
@Test
public void testIf()
{System.out.println(dao.selectByIf(User.builder().age((short) 33).name("111").build()));
}
7.2 <choose>
+<when>
+<otherwise>
相似 Java
中的 switch
语句:
<choose>
相似switch
<when>
相似case
<otherwise>
相似default
当其中一个 <when>
成立时,语句便完结,相似于主动加上了"break"
。
示例:
<select id="selectByChoose" parameterType="Integer" resultType="pers.init.entity.User">
select * from user where 1=1
<choose>
<when test="name != null and name !=''">
and name like concat('%',#{name},'%')
</when>
<when test="age != null and age>0">
and age=#{age}
</when>
<otherwise>
and id != 3
</otherwise>
</choose>
</select>
Dao
接口:
List<User> selectByChoose(User user);
测试:
@Test
public void testChoose()
{System.out.println(dao.selectByChoose(User.builder().age((short)33).build()));
}
7.3 <trim>
次要性能:
- 加前缀
- 加后缀
- 替换某些首部 / 尾部内容
这里是一个应用 <trim>
来实现 <where>
的例子:
<select id="selectByTrim" parameterType="Integer" resultType="pers.init.entity.User">
select * from user
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name !=''">
and name like concat('%',#{name},'%')
</if>
</trim>
</select>
Dao
接口:
List<User> selectByTrim(User user);
测试:
@Test
public void testTrim()
{System.out.println(dao.selectByTrim(User.builder().build()));
System.out.println(dao.selectByTrim(User.builder().name("test2").build()));
}
7.4 <where>
<where>
最罕用的就是拼接查问条件,比方有多个查问条件,仅仅应用多个 <if>
的话会呈现首个 <if>
有一个多余的 and
的问题,而应用 <where>
会进行智能解决,当然也对 or
实用,例子如下:
<select id="selectByWhere" parameterType="Integer" resultType="pers.init.entity.User">
select * from user
<where>
<if test="name != null and name !=''">
and name like concat('%',#{name},'%')
</if>
<if test="age != null and age>0">
and age=#{age}
</if>
</where>
</select>
Dao
接口:
List<User> selectByWhere(User user);
测试:
@Test
public void testWhere()
{System.out.println(dao.selectByWhere(User.builder().build()));
System.out.println(dao.selectByWhere(User.builder().name("111").build()));
System.out.println(dao.selectByWhere(User.builder().age((short)-3).build()));
}
7.5 <set>
<set>
个别配合 update
语句应用,比方:
<update id="updateBySet" parameterType="pers.init.entity.User">
update user
<set>
<if test="name != null and name !=''">
name = #{name},
</if>
<if test="age != null and age > 0">
age = #{age}
</if>
</set>
where id=#{id}
</update>
Dao
接口:
int updateBySet(User user);
测试:
@Test
public void testSet()
{System.out.println(dao.updateBySet(User.builder().name("999999").age((short)39).id(1).build()));
System.out.println(dao.selectByWhere(User.builder().build()));
}
7.6 <foreach>
<foreach>
次要用于 in
中,能够认为是一个汇合,典型的应用场景是select xxx from xxx where xxx in <foreach>
。
<foreach>
的次要属性有:
item
:每个元素的别名index
:每个元素的下标collection
:<foreach>
的类型,有list
、array
、map
三种,当传入单个参数且该参数类型为List
时,则为list
,传入单个参数且该参数类型为数组时,则为array
,否则应将其封装成Map
,并设置属性值为map
open
:语句开始标记close
:语句完结标记
例子:
<select id="selectByForeach" parameterType="Integer" resultType="pers.init.entity.User">
select * from user where id in
<foreach collection="list" item="item" index="index" open="(" separator="," close=")">#{item}</foreach>
</select>
Dao
接口:
List<User> selectByForeach(List<Integer> id);
测试:
@Test
public void testForeach()
{System.out.println(dao.selectByForeach(List.of(1,2,3)));
}
7.7 <bind>
<bind>
可用于对字符串进行拼接,对于字符串拼接,MySQL
应用的是 concat
,而Oracle
应用的是 ||
,而MyBatis
提供了 <bind>
能够屏蔽这种 DBMS
之间的差别,无需批改 xml
即可进行移植,例子如下:
<select id="selectByBind" parameterType="pers.init.entity.User" resultType="pers.init.entity.User">
<bind name="new_name" value="'%'+name+'%'"/>
select * from user where name like #{new_name}
</select>
Dao
接口:
List<User> selectByBind(User user);
测试:
@Test
public void testBind()
{System.out.println(dao.selectByBind(User.builder().name("test1").build()));
}
8 源码
此处给出了实现所有例子后的代码,仅供参考,但不倡议间接clone
,倡议从初始化工程开始逐渐实现。
Java
版:
- Github
- 码云
- CODE.CHINA
Kotlin
版:
- Github
- 码云
- CODE.CHINA