2.2 Mybatis 工具类(了解)
为了简化MyBatis的开发,可将MyBatis进一步封装。
java
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 MybatisUtil {
/**
* 不让用户在外界创建工具类对象
*/
private MybatisUtil() {
}
/**
* 初始化SqlSessionFactory对象
*/
private static SqlSessionFactory factory;
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取SqlSession对象的方法
*/
public static SqlSession getSession() {
return factory.openSession();
}
}
测试:
java
public class MybatisUtilTest {
@Test
public void test() {
SqlSession sqlSession = MybatisUtil.getSession();
System.out.println(sqlSession);
sqlSession.close();
}
}
2.4 Mapper代理方式(强制)
Mapper代理开发方式只需要程序员编写Mapper接口(相当于Dao接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
程序员编写Mapper接口需要遵循一些开发规范,MyBatis可以自动生成Mapper接口实现类代理对象
2.4.1 开发规范
1、Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、Mapper.xml中定义的每个标签的id与Mapper接口方法名相同。
3、Mapper.xml中定义的每个sql的parameterType的类型与Mapper接口方法的参数类型相同。
4、Mapper.xml中定义的每个sql的resultType的类型与Mapper接口方法返回值类型相同。
注:Mapper.xml映射文件最好和Mapper接口名称一致。
2.4.2 实体类
java
package org.example.mybatis.entity;
import java.util.Date;
/**
* Emp实体类
*/
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", comm=" + comm +
", deptno=" + deptno +
'}';
}
}
2.4.3 Mapper接口
java
package org.example.mybatis.mapper;
import org.example.mybatis.entity.Emp;
import java.util.List;
/*
* Mapper接口相当于我们之前写的Dao接口,只是在Mybatis里面我们习惯这么写而已。
*/
public interface Mapper {
List<Emp> select();
Emp selectById(Integer empno);
void insert(Emp emp);
int update(Emp emp);
boolean delete(Integer empno);
}
批量查询:方法返回值为List类型,表示SqlSession对象将调用selectList()方法。
单条查询:方法返回值为单个实体对象,表示SqlSession对象将调用selectOne()方法。
增删改:
方法返回值为void,表示SqlSession对象中insert,update,delete方法的返回值不做任何处理。
方法返回值为int类型,表示SqlSession对象中insert,update,delete方法的返回值直接返回。
方法返回值为boolean类型,表示根据SqlSession对象中的insert,update,delete方法返回值(影响数据库的条数)判断操作是否成功,如果影响数据库的条数大于0条,表示成功,否则表示失败。
2.4.4 mapper文件
<?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"> <!--namespace:与Mapper接口的全限定名保持一致--> <mapper namespace="org.example.mybatis.mapper.Mapper"> <!-- statementId与Mapper接口的方法名称保持一致; parameterType的类型必须与方法的参数类型保持一致; resultType的类型必须与方法的返回值类型保持一致; --> <select id="select" resultType="org.example.mybatis.entity.Emp"> select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp </select> <select id="selectById" parameterType="java.lang.Integer" resultType="org.example.mybatis.entity.Emp"> select empno, ename, job, hiredate, mgr, sal, comm, deptno from emp where empno = #{empno} </select> <insert id="insert" parameterType="org.example.mybatis.entity.Emp"> insert into emp(ename, job, mgr, hiredate, sal, comm, deptno) values (#{ename}, #{job}, #{mgr}, #{hiredate}, #{sal}, #{comm}, #{deptno}) </insert> <update id="update" parameterType="org.example.mybatis.entity.Emp"> update emp set ename=#{ename} , job=#{job} , mgr=#{mgr} , hiredate=#{hiredate} , sal=# {sal},comm=#{comm} , deptno=#{deptno} where empno=#{empno} </update> <delete id="delete" parameterType="java.lang.Integer"> delete from emp where empno = #{empno} </delete> </mapper>
加载mapper文件:
<?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 文件的根节点 --> <configuration> <!-- properties 用于引入外部的properties配置文件 resource:引入类路径下的文件 url:引入磁盘或网路 --> <properties/> <!-- environments:多个配置环境;通过default属性可以对多个环境快速切换。 environments default属性的值必须和某个environment的id值一致。 --> <!-- 和spring整合后 environments配置将废除,了解即可 --> <environments default="mysql"> <environment id="oracle"> <!-- 配置事务:使用jdbc的事务管理 --> <transactionManager type="JDBC"/> <!-- 配置数据源:连接数据库的信息 type: 表示连接是否使用连接池,POOLED表示mybatis中自带的连接池;JNDI、 POOLED、UNPOOLED --> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </dataSource> </environment> <environment id="mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <!-- 加载映射文件的位置 --> <mappers> <mapper resource="mapper/Mapper.xml"/> </mappers> </configuration>
2.4.5 测试
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis.entity.Emp;
import org.example.mybatis.mapper.Mapper;
import org.example.mybatis.utils.MybatisUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class MapperTest {
private SqlSession sqlSession;
private Mapper mapper;
@Before
public void before() {
sqlSession = MybatisUtil.getSession();
//获取Mapper接口的代理对象
mapper = sqlSession.getMapper(Mapper.class);
}
@After
public void after() {
sqlSession.commit();
sqlSession.close();
}
@Test
public void test() {
System.out.println(sqlSession);
System.out.println(mapper);
}
@Test
public void testSelect() {
List<Emp> list = mapper.select();
for (Emp emp : list) {
System.out.println(emp);
}
}
@Test
public void testSelectById() {
Emp emp = mapper.selectById(7938);
System.out.println(emp);
}
@Test
public void testInsert() {
Emp emp = new Emp();
emp.setEname("小明");
emp.setJob("职员");
emp.setSal(4500.0);
emp.setComm(1000.0);
emp.setMgr(1);
emp.setHiredate(new Date());
mapper.insert(emp);
}
@Test
public void testUpdate() {
Emp emp = new Emp();
emp.setEmpno(7940);
emp.setEname("小李");
emp.setJob("秘书");
emp.setSal(5300.0);
emp.setComm(1300.0);
emp.setMgr(1);
emp.setHiredate(new Date());
int result = mapper.update(emp);
System.out.println("方法的返回值:" + result);
}
@Test
public void testDelete() {
boolean result = mapper.delete(7940);
System.out.println("方法的返回值:" + result);
}
}
三、Mybatis 核心配置文件(了解)
MyBatis的全局配置文件mybatis-config.xml,配置内容如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
四、Mybatis Mapper配置文件
mapper.xml映射文件中定义了操作数据库的Sql,每个Sql是一个statement,映射文件是MyBatis的核心。
4.1 parameterType输入映射(强制)
parameterType配置输入参数的类型。
4.1.1 表结构
CREATE TABLE `users` ( `id` int(11) PRIMARY KEY AUTO_INCREMENT, `username` varchar(20), `password` varchar(50), `realname` varchar(20) ); INSERT INTO `users` VALUES (1, 'admin', '123456', '管理员'); INSERT INTO `users` VALUES (2, 'tom', '123', '汤姆'); INSERT INTO `users` VALUES (3, 'jerry', '456', '杰瑞'); INSERT INTO `users` VALUES (4, 'zhangsan', '111', '张三'); INSERT INTO `users` VALUES (5, 'lisi', '222', '李四');
4.1.2 实体类
java
package org.example.mybatis.entity;
public class User {
private Integer id;
private String username;
private String password;
private String realname;
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRealname() {
return realname;
}
public void setRealname(String realname) {
this.realname = realname;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", realname='" + realname + '\'' +
'}';
}
}
4.1.3 简单类型
Java基本数据类型以及包装类,String字符串类型。
mapper接口:
java
package org.example.mybatis.mapper;
import org.example.mybatis.entity.Users;
import java.util.List;
public interface UsersMapper {
List<Users> selectByRealname(String realname);
}
mapper文件:
<?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="org.example.mybatis.mapper.UsersMapper"> <select id="selectByRealname" parameterType="java.lang.String" resultType="org.example.mybatis.entity.Users"> select id, username, password, realname from users where realname like concat('%', #{realname}, '%') </select> </mapper>
测试:
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis.entity.Users;
import org.example.mybatis.mapper.UsersMapper;
import org.example.mybatis.utils.MybatisUtil;
import org.junit.Test;
import java.util.List;
public class ParameterTypeTest {
@Test
public void testSimpleParam() {
SqlSession sqlSession = MybatisUtil.getSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
List<Users> list = usersMapper.selectByRealname("张");
for (Users users : list) {
System.out.println(users);
}
sqlSession.close();
}
}
4.1.4 实体类或自定义类型
开发中通过实体类或pojo类型传递查询条件,查询条件是综合的查询条件,不仅包括实体类中查询条件
还包括其它的查询条件,这时可以使用包装对象传递输入参数。
4.1.4.1 自定义类型
分页类:
java
package org.example.mybatis.entity;
public class Page {
//当前页码
private Integer pageNum = 1;
//每页条数
private Integer pageSize = 3;
//总页数: 总记录数/每页条数,除不尽+1
private Integer pages;
//总记录数
private Integer total;
/**
* mysql
* 起始偏移量:(当前页码-1)*每页条数
*/
private Integer offset;
/**
* oracle
* 起始条数:(当前页码-1)*每页条数+1
* 结束条数: 当前页码*每页条数
*/
private Integer start;
private Integer end;
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getPages() {
return getTotal() % getPageSize() == 0 ? getTotal() / getPageSize() :
getTotal() / getPageSize() + 1;
}
public void setPages(Integer pages) {
this.pages = pages;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getOffset() {
return (getPageNum() - 1) * getPageSize();
}
public void setOffset(Integer offset) {
this.offset = offset;
}
public Integer getStart() {
return (getPageNum() - 1) * getPageSize() + 1;
}
public void setStart(Integer start) {
this.start = start;
}
public Integer getEnd() {
return getPageNum() * getPageSize();
}
public void setEnd(Integer end) {
this.end = end;
}
}
复合类:UsersQuery
java
package org.example.mybatis.entity;
/**
* 多条件查询复合类
*/
public class UsersQuery {
private Users users;
private Page page;
public Users getUsers() {
return users;
}
public void setUsers(Users users) {
this.users = users;
}
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
}
4.1.4.2 mapper接口
java
// 在UsersMapper接口上增加
List<Users> selectByPage(Page page);
List<Users> selectByRealnameAndPage(UsersQuery usersQuery);
4.1.4.3 mapper文件
<select id="selectByPage" parameterType="org.example.mybatis.entity.Page" resultType="org.example.mybatis.entity.Users"> select id,username,password,realname from users order by id limit #{offset}, #{pageSize} </select> <select id="selectByRealnameAndPage" parameterType="org.example.mybatis.entity.UsersQuery" resultType="org.example.mybatis.entity.Users"> select id,username,password,realname from users where realname like concat('%',#{users.realname},'%') order by id limit #{page.offset}, #{page.pageSize} </select>
4.1.4.4 测试
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis.entity.Page;
import org.example.mybatis.entity.Users;
import org.example.mybatis.entity.UsersQuery;
import org.example.mybatis.mapper.UsersMapper;
import org.example.mybatis.utils.MybatisUtil;
import org.junit.Test;
import java.util.List;
public class ClassParamTests {
@Test
public void testClassParam1() {
SqlSession sqlSession = MybatisUtil.getSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Page page = new Page();
page.setPageNum(1);
System.out.println("mysql起始偏移量:" + page.getOffset());
System.out.println("起始条数:" + page.getStart());
System.out.println("结束条数:" + page.getEnd());
List<Users> list = usersMapper.selectByPage(page);
for (Users users : list) {
System.out.println(users);
}
sqlSession.close();
}
@Test
public void testPojoParam2() {
SqlSession sqlSession = MybatisUtil.getSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Page page = new Page();
page.setPageNum(1);
Users users = new Users();
users.setRealname("张");
UsersQuery usersQuery = new UsersQuery();
usersQuery.setPage(page);
usersQuery.setUsers(users);
List<Users> list = usersMapper.selectByRealnameAndPage(usersQuery);
for (Users u : list) {
System.out.println(u);
}
sqlSession.close();
}
}
4.1.5 Map类型
mapper接口:
java
// 在UsersMapper接口上增加
List<Users> selectUseMap(Map<String, Object> map)
mapper文件:
<select id="selectUseMap" parameterType="java.util.HashMap" resultType="org.example.mybatis.entity.Users"> select id,username,password,realname from users where realname like concat('%',#{name},'%') order by id limit #{begin}, #{size} </select>
测试:
java
@Test
public void testMapParam() {
SqlSession sqlSession = MybatisUtil.getSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("name", "李");
map.put("size", 5);
map.put("begin", 0);
List<Users> list = usersMapper.selectUseMap(map);
for (Users u : list) {
System.out.println(u);
}
sqlSession.close();
}
4.1.6 多输入参数
MyBatis中允许有多个输入参数,可使用@Param注解。
这种做法类似与Map类型的输入参数,其中@Param注解的value属性值为Map的key,在映射文件中通
过ognl可获取对应的value,并且parameterType可以不指定类型。
mapper接口:
Users login(@Param("uname") String username, @Param("pwd") String password);
mapper文件:
<select id="login" parameterType="java.util.HashMap" resultType="org.example.mybatis.entity.Users"> select id,username,password,realname from users where username=#{uname} and password=#{pwd} </select>
测试:
java
@Test
public void testMultiParam() {
SqlSession sqlSession = MybatisUtil.getSession();
UsersMapper usersMapper = sqlSession.getMapper(UsersMapper.class);
Users users = usersMapper.login("jerry", "456");
System.out.println(users);
sqlSession.close();
}
4.2 resultType输出映射(强制)
4.2.1 表结构
CREATE TABLE `person` ( `id` int(11) PRIMARY KEY AUTO_INCREMENT, `person_name` varchar(20), `person_age` int(4), `person_address` varchar(50) ); INSERT INTO `person` VALUES (1, '曹操', 40, '洛阳'); INSERT INTO `person` VALUES (2, '刘备', 38, '成都'); INSERT INTO `person` VALUES (3, '孙权', 29, '杭州'); INSERT INTO `person` VALUES (4, '关羽', 35, '荆州'); INSERT INTO `person` VALUES (5, '张飞', 32, '成都'); INSERT INTO `person` VALUES (6, '曹仁', 28, '许都');
4.2.2 实体类
java
package org.example.mybatis.entity;
public class Person {
private Integer id;
private String personName;
private Integer personAge;
private String personAddress;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public Integer getPersonAge() {
return personAge;
}
public void setPersonAge(Integer personAge) {
this.personAge = personAge;
}
public String getPersonAddress() {
return personAddress;
}
public void setPersonAddress(String personAddress) {
this.personAddress = personAddress;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", personName='" + personName + '\'' +
", personAge=" + personAge +
", personAddress='" + personAddress + '\'' +
'}';
}
}
4.2.3 简单类型
查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。
mapper接口:
java
package org.example.mybatis.mapper;
public interface PersonMapper {
// 查询Person的总数量
Integer selectCount();
}
mapper文件:
<?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="org.example.mybatis.mapper.PersonMapper"> <select id="selectCount" resultType="java.lang.Integer"> select count(1) from person </select> </mapper>
测试:
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis.mapper.PersonMapper;
import org.example.mybatis.utils.MybatisUtil;
import org.junit.Test;
public class ResultTypeTest {
@Test
public void testSimpleResult() {
SqlSession sqlSession = MybatisUtil.getSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
int total = personMapper.selectCount();
System.out.println("总记录数:" + total);
sqlSession.close();
}
}
4.2.4 实体类对象和列表
不管是输出的实体类是单个对象还是一个列表(list中包括实体类对象),在mapper.xml中resultType指定的类型是一样的。
在原始Dao的方式中,通过selectOne和selectList方法来区分返回值为单个对象或集合列表,而在mapper代理中,则通过接口中定义的方法返回值来区分。
mapper接口:
java
Person selectById(Integer id);
List<Person> selectAll();
mapper文件:
<select id="selectById" parameterType="java.lang.Integer" resultType="org.example.mybatis.entity.Person"> select id,person_name personName,person_age personAge,person_address personAddress from person where id=#{id} </select> <select id="selectAll" resultType="org.example.mybatis.entity.Person"> select id,person_name personName,person_age personAge,person_address personAddress from person </select>
测试:
java
@Test
public void testResultType1() {
SqlSession sqlSession = MybatisUtil.getSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
Person person = personMapper.selectById(1);
System.out.println(person);
sqlSession.close();
}
@Test
public void testResultType2() {
SqlSession sqlSession = MybatisUtil.getSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> list = personMapper.selectAll();
for (Person person : list) {
System.out.println(person);
}
sqlSession.close();
}
4.2.5 resultMap
resultType可以指定将查询结果映射为实体类,但需要实体类的属性名和SQL查询的列名一致方可映射成功,当然如果开启下划线转驼峰 或 Sql设置列别名,也可以自动映射。
如果SQL查询字段名和实体类的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还会将查询结果映射到实体类对象中。
resultMap可以实现将查询结果映射为复合型的实体类,比如在查询结果映射对象中包括实体类和list实现一对一查询和一对多查询。
mapper接口:
java
List<Person> selectAll();
**mapper文件:**使用resultMap作为statement的输出映射类型。
<resultMap id="selectResultMap" type="org.example.mybatis.entity.Person"> <id property="id" column="id"/> <result property="personName" column="person_name"/> <result property="personAge" column="person_age"/> <result property="personAddress" column="person_address"/> </resultMap> <select id="select" resultMap="selectResultMap"> select id,person_name,person_age,person_address from person </select>
resultType: 自动映射。
resultMap: 手动映射。
id: 唯一标识,名称;
type: 手动映射的java类型;
子标签 <id/> 配置数据库表中的主键和实体类中属性的对应关系;
子标签 <result/> 配置数据库表中的普通字段和实体类中属性的对应关系;
property:实体类中的成员变量名;
column:结果集中的字段名称;
javaType:实体类成员变量的类型,由mybaits自动识别,可不配置;
jdbcType:表字段的类型,由mybaits自动识别,可不配置;
typeHandler:自定义类型处理器,用的相对比较少;
测试:
java
@Test
public void testResultMap() {
SqlSession sqlSession = MybatisUtil.getSession();
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
List<Person> list = personMapper.select();
for (Person person : list) {
System.out.println(person);
}
sqlSession.close();
}
4.3 动态SQL(强制)
4.3.1 什么是动态SQL
动态Sql是指MyBatis对Sql语句进行灵活操作,通过表达式进行判断,对Sql进行灵活拼接、组装。
比如:
我们要查询姓名中带 M 和 高于 1000的员工信息;
可能有时候我们需要不带条件查询;
可能有时候我们需要模糊查询;
可能有时候需要根据多条件查询;
动态SQL可以帮助我们解决这些问题。
通过Mybatis提供的各种标签方法实现动态拼接sql。
4.3.2 if标签
判断标签,当参数符合判断条件拼接SQL语句。
实体类:
java
package org.example.mybatis.entity;
import java.util.Date;
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
private String gender;
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Integer getMgr() {
return mgr;
}
public void setMgr(Integer mgr) {
this.mgr = mgr;
}
public Date getHiredate() {
return hiredate;
}
public void setHiredate(Date hiredate) {
this.hiredate = hiredate;
}
public Double getSal() {
return sal;
}
public void setSal(Double sal) {
this.sal = sal;
}
public Double getComm() {
return comm;
}
public void setComm(Double comm) {
this.comm = comm;
}
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", comm=" + comm +
", deptno=" + deptno +
", gender='" + gender + '\'' +
'}';
}
}
mapper接口:
java
package org.example.mybatis.mapper;
import org.example.mybatis.entity.Emp;
import java.util.List;
public interface EmpMapper {
List<Emp> selectUseIf(Emp emp);
}
mapper文件:
<?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="org.example.mybatis.mapper.EmpMapper"> <select id="selectUseIf" parameterType="org.example.mybatis.entity.Emp" resultType="org.example.mybatis.entity.Emp"> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp where <!-- 注意:判断条件中使用的变量为实体类或输入参数的属性 空字符串的判断仅能使用在字符串类型的属性中 --> <if test="ename != null and ename != ''"> ename like concat('%',#{ename},'%') </if> <if test="sal != null"> and sal=#{sal} </if> <if test="deptno != null"> and deptno=#{deptno} </if> </select> </mapper>
测试:
java
package org.example.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mybatis.entity.Emp;
import org.example.mybatis.mapper.EmpMapper;
import org.example.mybatis.utils.MybatisUtil;
import org.junit.Test;
import java.util.List;
/*
* 动态sql测试
*/
public class DynamicSqlTest {
@Test
public void testIf() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEname("S");
emp.setSal(1300.0);
emp.setDeptno(20);
List<Emp> list = empMapper.selectUseIf(emp);
for (Emp e : list) {
System.out.println(e);
}
sqlSession.close();
}
}
4.3.3 where标签
where标签,替代where关键字。
-
当where标签内所有的条件都不成立,不会拼接where关键字,只要有一个条件成立就会在SQL语句中拼接where关键字。
-
where标签会自动剔除条件头部的and或者or关键字。
mapper接口:
java
List<Emp> selectUseWhere(Emp emp);
mapper文件:
<select id="selectUseWhere" parameterType="org.example.mybatis.entity.Emp" resultType="org.example.mybatis.entity.Emp"> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp <where> <if test="ename != null and ename != ''"> ename like concat('%',#{ename},'%') </if> <if test="sal != null"> and sal=#{sal} </if> <if test="deptno != null"> and deptno=#{deptno} </if> </where> </select>
测试:
java
@Test
public void testWhere() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEname("S");
emp.setSal(1300.0);
emp.setDeptno(20);
List<Emp> list = empMapper.selectUseWhere(emp);
for (Emp e : list) {
System.out.println(e);
}
sqlSession.close();
}
4.3.4 set标签
set标签,替代set关键字。
- 当set标签内所有的条件都不成立,不会拼接set关键字,只要有一个条件成立就会在SQL语句中拼接set关键字。
注意:如果set包含的内容为空SQL语句会出错。
- set标签会自动剔除条件末尾的任何不相关的逗号。
mapper接口:
java
void updateUseSet(Emp emp);
mapper文件:
<update id="updateUseSet" parameterType="org.example.mybatis.entity.Emp"> update emp <set> <if test="ename != null"> ename=#{ename}, </if> <if test="job != null"> job=#{job}, </if> <if test="mgr != null"> mgr=#{mgr}, </if> <if test="hiredate != null"> hiredate=#{hiredate}, </if> <if test="sal != null"> sal=#{sal}, </if> <if test="comm != null"> comm=#{comm}, </if> <if test="deptno != null"> deptno=#{deptno}, </if> </set> where empno=#{empno} </update>
测试:
java
@Test
public void testSet() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEmpno(7938);
emp.setEname("JACK");
emp.setJob("MANAGER");
emp.setMgr(7844);
emp.setSal(5600.0);
emp.setComm(1200.0);
emp.setHiredate(new Date());
emp.setDeptno(30);
empMapper.updateUseSet(emp);
sqlSession.commit();
sqlSession.close();
}
4.3.5 trim标签
trim标签属性解析:
prefix:前缀,包含内容前加上某些字符。
suffix:后缀,包含内容后加上某些字符。
prefixOverrides:剔除包含内容前的某些字符。
suffixOverrides:剔除包含内容后的某些字符。
4.3.5.1 mapper接口
java
void insertUseTrim(Emp emp);
4.3.5.2 mapper文件
<!--trim标签--> <insert id="insertUseTrim" parameterType="org.example.mybatis.entity.Emp"> insert into emp <trim prefix="(" suffix=")" suffixOverrides=","> <if test="ename != null"> ename, </if> <if test="job != null"> job, </if> <if test="mgr != null"> mgr, </if> <if test="hiredate != null"> hiredate, </if> <if test="sal != null"> sal, </if> <if test="comm != null"> comm, </if> <if test="deptno != null"> deptno, </if> </trim> <trim prefix=" values(" suffix=")" suffixOverrides=","> <if test="ename != null"> #{ename}, </if> <if test="job != null"> #{job}, </if> <if test="mgr != null"> #{mgr}, </if> <if test="hiredate != null"> #{hiredate}, </if> <if test="sal != null"> #{sal}, </if> <if test="comm != null"> #{comm}, </if> <if test="deptno != null"> #{deptno}, </if> </trim> </insert>
4.3.5.3 测试
java
@Test
public void testTrim() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = new Emp();
emp.setEname("CHRIS");
emp.setJob("CLERK");
emp.setMgr(1);
emp.setSal(3400.0);
emp.setComm(800.0);
emp.setHiredate(new Date());
emp.setDeptno(10);
empMapper.insertUseTrim(emp);
sqlSession.commit();
sqlSession.close();
}
4.3.5.4 代替where标签
<select id="selectUseTrim" parameterType="org.example.mybatis.entity.Emp" resultType="org.example.mybatis.entity.Emp"> select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp <trim prefix="where" prefixOverrides="and|or"> <if test="ename != null and ename != ''"> ename like concat('%',#{ename},'%') </if> <if test="sal != null"> and sal=#{sal} </if> <if test="deptno != null"> and deptno=#{deptno} </if> </trim> </select>
4.3.5.5 代替set标签
<!--代替set标签--> <update id="updateUseTrim" parameterType="org.example.mybatis.entity.Emp"> update emp <trim prefix="set" suffixOverrides=","> <if test="ename != null"> ename=#{ename}, </if> <if test="job != null"> job=#{job}, </if> <if test="mgr != null"> mgr=#{mgr}, </if> <if test="hiredate != null"> hiredate=#{hiredate}, </if> <if test="sal != null"> sal=#{sal}, </if> <if test="comm != null"> comm=#{comm}, </if> <if test="deptno != null"> deptno=#{deptno}, </if> </trim> where empno=#{empno} </update>
4.3.6 foreach标签
向SQL传递数组或list,MyBatis使用foreach解析。
属性解析:
collection: 遍历的数组或集合对象名称。
SQL只接收一个数组参数,这时SQL解析参数的名称MyBatis固定为array。
SQL只接收一个List参数,这时SQL解析参数的名称MyBatis固定为list。
如果是通过一个实体类或自定义类型的属性传递到SQL的数组或List集合,则参数的名称为实体类或自定义类型中的属性名。
index: 为数组的下标。
item: 每次遍历生成的对象。
open: 开始遍历时拼接的串。
close: 结束遍历时拼接的串。
separator: 遍历的两个对象中需要拼接的串。
mapper接口:
java
void deleteUseForeach(Integer[] ids);
void insertUseForeach(List<Emp> empList);
mapper文件:
<!--foreach标签--> <delete id="deleteUseForeach" parameterType="java.lang.Integer"> <!--delete from emp where empno in (1,2,3,4)--> delete from emp where empno in <foreach collection="array" open="(" close=")" separator="," item="id"> #{id} </foreach> </delete> <insert id="insertUseForeach" parameterType="org.example.mybatis.entity.Emp"> insert into emp(ename,job,mgr,hiredate,sal,comm,deptno) values <foreach collection="list" separator="," item="emp"> (#{emp.ename},#{emp.job},#{emp.mgr},#{emp.hiredate},#{emp.sal},#{emp.comm},#{emp.deptno}) </foreach> </insert>
测试:
java
@Test
public void testForeach() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
empMapper.deleteUseForeach(new Integer[]{1, 2, 3, 4});
sqlSession.commit();
sqlSession.close();
}
@Test
public void testForeach2() {
SqlSession sqlSession = MybatisUtil.getSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> empList = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
Emp emp = new Emp();
emp.setEname("TOM" + i);
emp.setJob("CLERK" + i);
emp.setMgr(1);
emp.setSal(4567.0);
emp.setComm(123.0);
emp.setHiredate(new Date());
emp.setDeptno(10);
empList.add(emp);
}
empMapper.insertUseForeach(empList);
sqlSession.commit();
sqlSession.close();
}
4.3.7 choose标签
choose标签、when标签、otherwise标签的组合,类似于if-else-if判断。
<select id=""> select... <choose> <when test=""> </when> <when test=""> </when> <otherwise> </otherwise> </choose> </select>
4.3.8 SQL片段
将实现的动态SQL判断代码块抽取出来,组成一个SQL片段,其它的statement中就通过 <include> 标
签就可以引用SQL片段,方便程序员进行开发。
注意:在SQL片段中不要包括where标签。
<sql id="feildSql"> empno ,ename,job,mgr,hiredate,sal,comm,deptno </sql> <sql id="whereSql"> <if test="ename != null and ename != ''"> ename like concat('%',#{ename},'%') </if> <if test="sal != null"> and sal=#{sal} </if> <if test="deptno != null"> and deptno=#{deptno} </if> </sql> <select id="selectUseSql" parameterType="org.example.mybatis.entity.Emp" resultType="org.example.mybatis.entity.Emp"> select <include refid="feildSql"></include> from emp <where> <include refid="whereSql"></include> </where> </select>
4.4 Map输入和输出类型(强制)
实体类:
java
package org.example.mybatis.entity;
public class Dept {
private Integer deptno;
private String dname;
private String loc;
public Dept() {
}
public Dept(Integer deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
/**
* 获取
* @return deptno
*/
public Integer getDeptno() {
return deptno;
}
/**
* 设置
* @param deptno
*/
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
/**
* 获取
* @return dname
*/
public String getDname() {
return dname;
}
/**
* 设置
* @param dname
*/
public void setDname(String dname) {
this.dname = dname;
}
/**
* 获取
* @return loc
*/
public String getLoc() {
return loc;
}
/**
* 设置
* @param loc
*/
public void setLoc(String loc) {
this.loc = loc;
}
public String toString() {
return "Dept{deptno = " + deptno + ", dname = " + dname + ", loc = " + loc + "}";
}
}
Mapper接口:
java
package org.example.mybatis.mapper;
import java.util.List;
import java.util.Map;
public interface DeptMapper {
//查询
List<Map<String, Object>> queryAllDept();
//插入
int insertDept(Map<String, Object> map);
}
Mapper文件:
<?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="org.example.mybatis.mapper.DeptMapper"> <!-- 查询,输出参数是Map Mybatis会把查询结果给我们封装成List<Map> 因为一行数据就是一个Map --> <select id="queryAllDept" resultType="hashmap"> select * from dept </select> <!--插入,输入参数是Map--> <!--输入参数是hashmap,那么#{map中的key}--> <insert id="insertDept" parameterType="hashmap"> insert into dept values (#{id}, #{name}, #{address}) </insert> </mapper>
测试:
java
@Test
public void testMapResultType(){
// 获取Mapper接口的代理对象 (相当于实现类对象)
SqlSession sqlSession = MybatisUtil.getSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
//我们的输出参数是hashMap,那么mybatis会把查询结果集的列作为key,值作为value
List<Map<String, Object>> maps = mapper.queryAllDept();
for(Map<String, Object> map:maps){
System.out.println(map.get("DEPTNO")+"\t"+map.get("DNAME")+"\t"+map.get("LOC"))
;
}
sqlSession.close();
}
@Test
public void testMapParameter(){
// 获取Mapper接口的代理对象 (相当于实现类对象)
SqlSession sqlSession = MybatisUtil.getSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Map<String,Object> map = new HashMap<String,Object>();
map.put("id",50);
map.put("name","开发部门");
map.put("address","北京市");
int i = mapper.insertDept(map);
System.out.println(i);
sqlSession.close();
}