Mybatis是ORM持久层框架的佼佼者,它封装了JDBC,真正实现了SQL语句与Java代码的分离,它优秀的功能为:支持动态SQL,缓存,物理分页插件PageHelper。注意本篇介绍以数组为传递参数进行数据的查询它在实际运用中有两个例子:1、SQL语句中使用IN的情况,可以使用数组封装IN中的值;2、批量操作数据的情况,可以把操作的数据封装在数组中。

第一步:新建一个Maven项目mybatis_parameter(不使用模板),其中pom.xml配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.envy</groupId>
<artifactId>mybatis_parameter</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<!--pageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
</dependencies>
</project>

第二步:创建数据库mybatis_parameter和表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
drop table if exists person;
create table person(
id int PRIMARY key auto_increment,
username varchar(20),
email varchar(20),
gender varchar(20),
department_id int
);

drop table if exists department;
create table department(
id int primary key auto_increment,
department_name varchar(50)
);

alter table person add constraint fk_person_department foreign key(department_id) references department(id);

第三步:配置db.propertiesmybatis-config.xmllogj.properties文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
*******************************db.properties*****************************************

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis_parameter?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root

*******************************logj.properties*****************************************

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p [%t] %37c %3x - %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">
<configuration>

<!-- properties配置,用于加载外部的properties配置文件 -->
<properties resource="db.properties"></properties>


<!-- 配置配置项 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!--别名-->
<typeAliases>
<package name="com.envy.bean"/>
</typeAliases>


<!--配置数据源-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>

<!--
mappers主要用于配置我们外部的映射配置文件,在主配置文件中需要引入加载映射配置文件
-->
<mappers>
<!--
mapper主要配置引入某一个具体的mapper映射文件,通常是在resource文件下以路径方式进行引入
-->
<mapper resource="mapper/PersonMapper.xml"/>
</mappers>

</configuration>

第四步:新建com.envy.daocom.envy.beancom.envy.testcom.envy.interceptor,接着在bean中新建两个Bean类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
*******************************Person.java*****************************************

package com.envy.bean;

public class Person {
private Integer id;
private String username;
private String email;
private String gender;
private Integer departmentId;
private Department department;

public Person(){}

public Integer getDepartmentId() {
return departmentId;
}

public void setDepartmentId(Integer departmentId) {
this.departmentId = departmentId;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {
this.department = department;
}
}


*******************************Department.java*****************************************

package com.envy.bean;

import java.util.List;

public class Department {
private Integer id;
private String departmentName;
private List<Person> personList;

public Department(){}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getDepartmentName() {
return departmentName;
}

public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}

public List<Person> getPersonList() {
return personList;
}

public void setPersonList(List<Person> personList) {
this.personList = personList;
}
}

第五步:在dao包中新建一个接口PersonMapper.java,接着在resource包中新建一个mapper包然后在mapper包中新建PersonMapper.xml文件。

完成了准备工作后开始进行介绍mybatis中的参数传递等知识。

Mybatis参数传递介绍

注意本篇介绍以数组为传递参数进行数据的查询。mybatis对于参数处理有三种情况:1、传递单个参数的形式,mybatis会自动进行参数的赋值;2、传递多个参数的形式,mybatis会自动封装在Map集合中;3、传递Collection、List、Array等参数的形式,mybatis会根据一定的规则封装在Map集合中。

Mybatis核心的API操作流程如下图所示:

传递单个参数

传递单个参数时,mybatis会直接取出参数值给mapper文件赋值,如id=#{id}

接口PersonMapper.java代码如下:

1
2
3
4
5
package com.envy.dao;

public interface PersonMapper {
void deletePerson (Integer id);
}

PersonMapper.xml文件中的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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="com.envy.dao.PersonMapper">
<resultMap id="BaseResultMap" type="com.envy.bean.Person">
<id column="id" property="id" javaType="Integer"/>
<result property="username" column="username" javaType="String"/>
<result property="email" column="email" javaType="String"/>
<result property="gender" column="gender" javaType="String"/>
<result property="departmentId" column="department_id" javaType="Integer"/>
<result property="username" column="username" javaType="String"/>
<!--属性关联,这里使用department_id来获取department信息,使用select属性来调用之前DepartmentDao中定义的sql方法-->
<association property="department" column="department_id" javaType="Department" select="com.envy.dao.DepartmentMapper.searchById"/>
</resultMap>

<delete id="deletePerson" parameterType="Integer">
delete from person where id =#{id}
</delete>
</mapper>

从前面的图中可以看出Mybatis需要先使用SqlSessionFactoryBuilderbuild方法来构造SqlSessionFactory对象,SqlSessionFactory对象其实就相当于数据库,因此最好是使用单例模式来创建该对象。mybatis_parameter\src\main\java\com\envy\test\ParameterTest.java文件中新建getSqlSessionFactory方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static SqlSessionFactory sqlSessionFactory=null;

public static SqlSessionFactory getSqlSessionFactory(){
if(sqlSessionFactory==null){
String resource = "mybatis-config.xml";
try {
InputStream is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
return sqlSessionFactory;
}

接着新建一个deletePerson方法来调用它,并执行相应的方法:

1
2
3
4
5
6
7
8
9
@Test
public void deletePerson(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
personMapper.deletePerson(6);
//mybatis的事务不会自动提交,因此需要借助于SqlSession的commit方法去提交事务
sqlSession.commit();
}

运行该方法,发现数据库已经成功的将id为6的Person对象给删除了。注意以下代码:

1
2
3
<delete id="deletePerson" parameterType="Integer">
delete from person where id =#{id}
</delete>

这个id =#{id}中前者id是数据库中的属性,后者id是deletePerson方法的形参,不是实参,因此修改为其他的名字,该代码也是可以正常运行的。

传递多个参数

先按照之前传递单个参数的想法来试试(多个参数之间用逗号隔开),看看能不能运行成功。

接口PersonMapper.java新增代码如下:

1
Person selectByUserNameAndGender(String username,String gender);

PersonMapper.xml文件中新增代码如下:

1
2
3
<select id="selectByUserNameAndGender" resultType="Person">
select * from person where username=#{username} and gender = #{gender}
</select>

ParameterTest.java文件新增selectByUserNameAndGender方法:

1
2
3
4
5
6
7
8
@Test
public void selectByUserNameAndGender(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.selectByUserNameAndGender("jack","男");
System.out.println(person);
}

运行结果如下:

1
Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]

意思就是说mybatis中没有找到你定义的username参数,只有四个参数:[arg1, arg0, param1, param2],那尝试将其修改为这些参数(注意此处为查询操作,因此需要在实体类中新增toString方法,否则会造成阅读障碍):

1
2
3
<select id="selectByUserNameAndGender" resultType="Person">
select * from person where username=#{param1} and gender=#{param2}
</select>

运行结果:

1
Person{id=1, username='jack', email='jack@qq.com', gender='男', departmentId=1, department=null}

第一种:JavaBean传递参数,其实也就是封装POJO类。前提是多个参数是业务逻辑的数据模型,可以直接传入POJO,在xml文件中使用#{属性名}取出传入POJO的属性值。

PersonMapper.java文件中的selectByUserNameAndGender方法修改为:

1
2
//封装成POJO对象
Person selectByUserNameAndGender(Person person);

PersonMapper.xml文件中新增代码如下:

1
2
3
4
<!--封装成POJO对象-->
<select id="selectByUserNameAndGender" resultType="Person">
select * from person where username=#{username} and gender=#{gender}
</select>

ParameterTest.java文件中的selectByUserNameAndGender方法修改为(注意需要去Person实体类中创建只含有username和gender参数的构造方法):

1
2
3
4
5
6
7
8
9
10
//第一种封装成POJO对象
@Test
public void selectByUserNameAndGender(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.selectByUserNameAndGender(new Person("jack","男"));
System.out.println(person);
sqlSession.commit();
}

运行结果如下:

1
Person{id=1, username='jack', email='jack@qq.com', gender='男', departmentId=1, department=null}

第二种:Map处理。前提是参数的个数比较少,且没有对应的JavaBean,那么可以封装成Map对象。在xml文件中使用#{key}取出map中所对应的值。

PersonMapper.java文件中的selectByUserNameAndGender方法修改为:

1
2
//封装成Map
Person selectByUserNameAndGender(Map<String,Object> param);

PersonMapper.xml文件中新增代码如下:

1
2
3
4
   <!--第二种:封装成Map对象-->
<select id="selectByUserNameAndGender" resultType="Person">
select * from person where username=#{name} and gender=#{gender}
</select>

ParameterTest.java文件中的selectByUserNameAndGender方法修改为(注意此时map的key值可以不和数据库中字段的属性值一一对应,但是xml文件中的#{key}必须是和map中的key是一致的):

1
2
3
4
5
6
7
8
9
10
11
12
    //第二种封装成Map对象
@Test
public void selectByUserNameAndGender(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Map<String,Object> param = new HashMap<String, Object>();
param.put("name","bob");
param.put("gender","男");
Person person = personMapper.selectByUserNameAndGender(param);
System.out.println(person);
}

运行结果如下:

1
Person{id=2, username='bob', email='bob@qq.com', gender='男', departmentId=1, department=null}

第三种:注解@Param。由于以上两种方式都需要手动创建Map及对象,不够简洁,可以使用@Param注解,它可以很明确的指定封装参数时map的key。

PersonMapper.java文件中的selectByUserNameAndGender方法修改为:

1
2
//使用Param注解
Person selectByUserNameAndGender(@Param("UserName")String username,@Param("Gender")String gender);

PersonMapper.xml文件中新增代码如下(注意此处username=#{UserName}中的UserName必须是前面@Param注解中使用到的,通常情况下该名称和参数名称保持一致,如此处最好是username,但是为了更清楚的认识,这里就设置不同):

1
2
3
4
<!--第三种:使用Param注解-->
<select id="selectByUserNameAndGender" resultType="Person">
select * from person where username=#{UserName} and gender=#{Gender}
</select>

ParameterTest.java文件中的selectByUserNameAndGender方法修改为:

1
2
3
4
5
6
7
8
9
//第三种使用@Param注解
@Test
public void selectByUserNameAndGender(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.selectByUserNameAndGender("marry","女");
System.out.println(person);
}

运行结果如下:

1
Person{id=3, username='marry', email='marry@qq.com', gender='女', departmentId=2, department=null}

有些人比较粗心,第一个参数前使用了注解,第二个参数前没有,那么在xml文件中,第一个还是可以使用注解中的值,但是第二个就必须使用前面四个中的某一个[arg0,arg1, param1, param2],至于具体哪一个则需要判断了。

传递Collection、List、Array等参数

传递Collection、List、Array等参数时式,mybatis会根据一定的规则封装在Map集合中。其实针对不同的参数,mybatis提供了不同的方式。

当参数类型为Collection接口时,转换为Map,Map的key为collection(mybatis版本3.3之后);当参数类型为List接口时,除collection的值外,还可以使用list作为key(mybatis版本3.2之前含);当参数类型为数组时,转换为Map,Map的key为array。

PersonMapper.java文件中新增getPersonByCollection方法:

1
Person getPersonByCollection(Collection list);

PersonMapper.xml文件中新增代码如下(注意此处的#{collection[0]}也可以修改为#{list[0]}其实就是取的第一个元素):

1
2
3
 <select id="getPersonByCollection" resultType="Person">
select * from person where id = #{collection[0]}
</select>

ParameterTest.java文件中新增getPersonByCollection方法:

1
2
3
4
5
6
7
8
@Test
public void getPersonByCollection(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.getPersonByCollection(Arrays.asList(1,2,3));
System.out.println(person);
}

运行结果如下:

1
Person{id=1, username='jack', email='jack@qq.com', gender='男', departmentId=1, department=null}

上面演示的就是第一和第二种情况,接下来是第三种参数是数组的情况:
修改PersonMapper.java文件中的getPersonByCollection方法为:

1
Person getPersonByCollection(int[] ids); 

修改PersonMapper.xml文件中id="getPersonByCollection"的select选项为:

1
2
3
<select id="getPersonByCollection" resultType="Person">
select * from person where id = #{array[0]}
</select>

修改ParameterTest.java文件中的getPersonByCollection方法为:

1
2
3
4
5
6
7
8
@Test
public void getPersonByCollection(){
//SqlSessionFactory相当于数据库,SqlSession相当于一次连接
SqlSession sqlSession = getSqlSessionFactory().openSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.getPersonByCollection(new int[]{1,2,3});
System.out.println(person);
}

运行结果如下:

1
Person{id=1, username='jack', email='jack@qq.com', gender='男', departmentId=1, department=null}

其实还可以通过@Param注解来完成参数为集合类型的传递,这样就不需要考虑是用collection/array/list了,根据注解来即可,相应的步骤如下:

1、修改PersonMapper.java文件中的getPersonByCollection方法为:

1
Person getPersonByCollection(@Param("test")int[] ids);

修改PersonMapper.xml文件中id="getPersonByCollection"的select选项为:

1
2
3
<select id="getPersonByCollection" resultType="Person">
select * from person where id = #{test[0]}
</select>

运行发现结果和之前的一致。

入参总结

接下来简单总结一下mybatis的入参处理:1、使用map传递参数时,会造成业务的可读性大大的降低;2、使用@Param注解处理受到参数个数(n)的影响,当n<5时建议使用@Param注解;当n>=5时考虑使用JavaBean方法传递。

mybatis中的动态SQL

mybatis支持动态SQL,使用foreach元素对数组、List、Set等进行循环遍历。一般用在数据库中字典相关内容的查找。foreach常用配置如下:1、collection属性的值有三个分别是list、array和map,分别对应的参数类型为:List、数组、map集合;2、item表示在迭代过程中每一个元素的别名;3、index表示在迭代过程中每次迭代到的位置(也就是下标,从零开始,map是其key);4、open是指前缀;5、close是指后缀;6、separator是分隔符表示迭代时每个元素之间以什么分隔。

注意select标签是先使用foreach元素对传进来的对象进行遍历以后,再与之前的sql语句进行拼接,最后再执行完整的sql语句。

举个例子,现在有一个int[] ids={1,2,3,4}现在遍历它的方式如下:

1
2
3
4
5
6
<select id="getListPersonByIds" resultType="Person">
select * from person where id in
<foreach collection="array" item="id" index="i" open="(" close=")" separator=",">
#{id}
</foreach>
</select>

这个代码最后就变成了SQL语句select * from person where id in (1,2,3,4);

总结一下在mybatis中以数组形式进行参数传递时,需要在Mapper文件中使用foreach元素对其进行遍历以后拼接成SQL,才会执行SQL。