共计 27300 个字符,预计需要花费 69 分钟才能阅读完成。
在之前咱们学习了 mybatis 的全局配置文件,上面咱们开始学习 mybatis 的映射文件,在映射文件中,能够编写以下的顶级元素标签:
cache – 该命名空间的缓存配置。cache-ref – 援用其它命名空间的缓存配置。resultMap – 形容如何从数据库后果集中加载对象,是最简单也是最弱小的元素。parameterMap – 老式格调的参数映射。此元素已被废除,并可能在未来被移除!请应用行内参数映射。文档中不会介绍此元素。sql – 可被其它语句援用的可重用语句块。insert – 映射插入语句。update – 映射更新语句。delete – 映射删除语句。select – 映射查问语句。
在每个顶级元素标签中能够增加很多个属性,上面咱们开始具体理解下具体的配置。
1、insert、update、delete 元素
属性 | 形容 |
---|---|
id |
在命名空间中惟一的标识符,能够被用来援用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 能够通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于援用内部 parameterMap 的属性,目前已被废除。请应用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只有语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异样之前,驱动程序期待数据库返回申请后果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 别离应用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅实用于 insert 和 update)这会令 MyBatis 应用 JDBC 的 getGeneratedKeys 办法来取出由数据库外部生成的主键(比方:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的主动递增字段),默认值:false。 |
keyProperty |
(仅实用于 insert 和 update)指定可能惟一辨认对象的属性,MyBatis 会应用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,能够用逗号分隔多个属性名称。 |
keyColumn |
(仅实用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,能够用逗号分隔多个属性名称。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配以后 databaseId 的语句;如果带和不带的语句都有,则不带的会被疏忽。 |
<!-- 如果数据库反对自增能够应用这样的形式 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user(user_name) values(#{userName})
</insert>
<!-- 如果数据库不反对自增的话,那么能够应用如下的形式进行赋值查问 -->
<insert id="insertUser2" >
<selectKey order="BEFORE" keyProperty="id" resultType="integer">
select max(id)+1 from user
</selectKey>
insert into user(id,user_name) values(#{id},#{userName})
</insert>
2、select 元素
1、select 的参数传递
<!--
当查问语句中蕴含多个参数的是,如果应用 #{属性名称} 就无奈获取具体的值了,那么应该如何应用呢?上面就是 mybatis 的参数传递形式
1、如果是单个参数,根本类型:应用 #{轻易写}
援用类型:应用 #{类的属性名称}
2、多个参数:当查问的时候传入多个参数的时候,就无奈简略的通过 #{参数名} 来获取值了,只能通过 arg0,arg1... 或者 param1,param2 等形式来获取值
起因就在于,mybatis 在传入多个参数的时候,会将这些参数封装到一个 map 中,此时 map 中的 key 就是
arg0,arg1,param1,param2 这些值,然而很显著,这样的传值形式不是很敌对,没有方法依据参数的名称来
获取具体的值,因而能够应用如下的形式来指定参数的 key 是什么
Emp selectEmpByNoAndName(@Param("empno") Integer empno, @Param("ename") String ename);
也就是通过 @Param 来指定存入 map 中的 key 值是什么
3、应用 map 来传递参数:仍然是间接应用 #{key} 来获取具体的属性值
-->
<select id="selectEmpByNoAndName" resultType="com.mashibing.bean.Emp">
select * from emp where empno=#{empno} and ename=#{ename}
</select>
<select id="selectEmpByNoAndName2" resultType="com.mashibing.bean.Emp">
select * from emp where empno=#{empno} and ename=#{ename}
</select>
2、参数的取值形式
在 xml 文件中编写 sql 语句的时候有两种取值的形式,别离是 #{} 和 ${},上面来看一下他们之间的区别:
<!--
当应用 #{} 来获取值的时候会发现打印的 sql 语句如下:select * from emp where empno=? and ename=?
当应用 ${} 来获取值的时候会发现打印的 sql 语句如下:select * from emp where empno=7369 and ename='SMITH'
通过刚刚的案例大家曾经发现了存在的问题了,应用 #{} 形式进行取值:采纳的是参数预编译的形式,参数的地位应用?进行代替,不会呈现 sql 注入的问题
应用 ${} 形式进行取值:采纳的是间接跟 sql 语句进行拼接的形式
此处大家须要留神,如果咱们的 sql 语句中的某些值不反对参数预编译,那么就必须要应用 ${} 的形式来取值了
-->
<select id="selectEmpByNoAndName" resultType="com.mashibing.bean.Emp">
select * from #{t} where empno=${empno} and ename=${ename}
</select>
3、解决汇合返回后果
EmpDao.xml
<!-- 当返回值的后果是汇合的时候,返回值的类型仍然写的是汇合中具体的类型 -->
<select id="selectAllEmp" resultType="com.mashibing.bean.Emp">
select * from emp
</select>
<!-- 在查问的时候能够设置返回值的类型为 map,当 mybatis 查问实现之后会把列的名称作为 key
列的值作为 value,转换到 map 中
-->
<select id="selectEmpByEmpReturnMap" resultType="map">
select * from emp where empno = #{empno}
</select>
<!-- 留神,当返回的后果是一个汇合对象的是,返回值的类型肯定要写汇合具体 value 的类型
同时在 dao 的办法上要增加 @MapKey 的注解,来设置 key 是什么后果
@MapKey("empno")
Map<Integer,Emp> getAllEmpReturnMap();-->
<select id="getAllEmpReturnMap" resultType="com.mashibing.bean.Emp">
select * from emp
</select>
UserDao.java
package com.mashibing.dao;
import com.mashibing.bean.Emp;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface EmpDao {public Emp findEmpByEmpno(Integer empno);
public int updateEmp(Emp emp);
public int deleteEmp(Integer empno);
public int insertEmp(Emp emp);
Emp selectEmpByNoAndName(@Param("empno") Integer empno, @Param("ename") String ename,@Param("t") String tablename);
Emp selectEmpByNoAndName2(Map<String,Object> map);
List<Emp> selectAllEmp();
Map<String,Object> selectEmpByEmpReturnMap(Integer empno);
@MapKey("empno")
Map<Integer,Emp> getAllEmpReturnMap();}
4、自定义后果集 —resultMap
Dog.java
package com.mashibing.bean;
public class Dog {
private Integer id;
private String name;
private Integer age;
private String gender;
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
public String getGender() {return gender;}
public void setGender(String gender) {this.gender = gender;}
@Override
public String toString() {
return "Dog{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
dog.sql
/*
Navicat MySQL Data Transfer
Source Server : node01
Source Server Version : 50729
Source Host : 192.168.85.111:3306
Source Database : demo
Target Server Type : MYSQL
Target Server Version : 50729
File Encoding : 65001
Date: 2020-03-24 23:54:22
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `dog`
-- ----------------------------
DROP TABLE IF EXISTS `dog`;
CREATE TABLE `dog` (`id` int(11) NOT NULL AUTO_INCREMENT,
`dname` varchar(255) DEFAULT NULL,
`dage` int(11) DEFAULT NULL,
`dgender` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of dog
-- ----------------------------
INSERT INTO dog VALUES ('1', '大黄', '1', '雄');
INSERT INTO dog VALUES ('2', '二黄', '2', '雌');
INSERT INTO dog VALUES ('3', '三黄', '3', '雄');
DogDao.java
package com.mashibing.dao;
import com.mashibing.bean.Dog;
public interface DogDao {public Dog selectDogById(Integer id);
}
DogDao.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="com.mashibing.dao.DogDao">
<!--
在应用 mybatis 进行查问的时候,mybatis 默认会帮咱们进行后果的封装,然而要求列名跟属性名称一一对应上
在理论的应用过程中,咱们会发现有时候数据库中的列名跟咱们类中的属性名并不是一一对应的,此时就须要起别名
起别名有两种实现形式:1、在编写 sql 语句的时候增加别名
2、自定义封装后果集
-->
<!-- 依据查问的数据进行后果的封装要应用 resultMap 属性,示意应用自定义规定 -->
<select id="selectDogById" resultMap="myDog">
select * from dog where id = #{id}
</select>
<!-- 自定义后果集,将每一个列的数据跟 javaBean 的对象属性对应起来
type: 示意为哪一个 javaBean 对象进行对应
id: 惟一标识,不便其余属性标签进行援用
-->
<resultMap id="myDog" type="com.mashibing.bean.Dog">
<!--
指定主键列的对应规定:column:示意表中的主键列
property: 指定 javaBean 的属性
-->
<id column="id" property="id"></id>
<!-- 设置其余列的对应关系 -->
<result column="dname" property="name"></result>
<result column="dage" property="age"></result>
<result column="dgender" property="gender"></result>
</resultMap>
<!-- 能够在 sql 语句中写别名 -->
<!-- <select id="selectDogById" resultType="com.mashibing.bean.Dog">
select id id,dname name,dage age,dgender gender from dog where id = #{id}
</select>-->
<!-- 这种形式是查问不到任何后果的,因为属性名跟列名并不是一一对应的 -->
<!-- <select id="selectDogById" resultType="com.mashibing.bean.Dog">
select * from dog where id = #{id}
</select>-->
</mapper>
5、联结查问
emp.java
package com.mashibing.bean;
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 common;
private Dept dept;
public Emp() {}
public Emp(Integer empno, String ename) {
this.empno = empno;
this.ename = ename;
}
public Emp(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double common, Dept dept) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.mgr = mgr;
this.hiredate = hiredate;
this.sal = sal;
this.common = common;
this.dept = dept;
}
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 getCommon() {return common;}
public void setCommon(Double common) {this.common = common;}
public Dept getDept() {return dept;}
public void setDept(Dept dept) {this.dept = dept;}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", mgr=" + mgr +
", hiredate=" + hiredate +
", sal=" + sal +
", common=" + common +
", dept=" + dept +
'}';
}
}
Dept.java
package com.mashibing.bean;
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;
}
public Integer getDeptno() {return deptno;}
public void setDeptno(Integer deptno) {this.deptno = deptno;}
public String getDname() {return dname;}
public void setDname(String dname) {this.dname = dname;}
public String getLoc() {return loc;}
public void setLoc(String loc) {this.loc = loc;}
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
'}';
}
}
EmpDao.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">
<!--namespace: 编写接口的全类名,就是通知要实现该配置文件是哪个接口的具体实现 -->
<mapper namespace="com.mashibing.dao.EmpDao">
<!-- 再做查问的时候,有时候须要关联其余对象,因而须要应用关联查问
能够通过上面自定义后果集的形式实现
-->
<select id="selectEmpAndDept" resultMap="empDept">
select * from emp left join dept on emp.deptno = dept.deptno where empno = #{empno};
</select>
<resultMap id="empDept" type="com.mashibing.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<result column="deptno" property="dept.deptno"></result>
<result column="dname" property="dept.dname"></result>
<result column="loc" property="dept.loc"></result>
</resultMap>
<!-- 在 mybatis 中还提供了一种简略的模式,应用 association 标签能够搞定
-->
<resultMap id="empDept" type="com.mashibing.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<association property="dept" javaType="com.mashibing.bean.Dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
</association>
</resultMap>
</mapper>
Test
@Test
public void test08() {
// 获取数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
try {EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp emp = mapper.selectEmpAndDept(7369);
System.out.println(emp);
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
6、获取汇合元素
Dept.java
package com.mashibing.bean;
import java.util.List;
public class Dept {
private Integer deptno;
private String dname;
private String loc;
private List<Emp> emps;
public Dept() {}
public Dept(Integer deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
public Integer getDeptno() {return deptno;}
public void setDeptno(Integer deptno) {this.deptno = deptno;}
public String getDname() {return dname;}
public void setDname(String dname) {this.dname = dname;}
public String getLoc() {return loc;}
public void setLoc(String loc) {this.loc = loc;}
public List<Emp> getEmps() {return emps;}
public void setEmps(List<Emp> emps) {this.emps = emps;}
@Override
public String toString() {
return "Dept{" +
"deptno=" + deptno +
", dname='" + dname + '\'' +
", loc='" + loc + '\'' +
", emps=" + emps +
'}';
}
}
DeptDao.java
package com.mashibing.dao;
import com.mashibing.bean.Dept;
import com.mashibing.bean.Emp;
import java.util.List;
public interface DeptDao {public Dept getDeptAndEmps(Integer deptno);
}
DeptDao.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="com.mashibing.dao.DeptDao">
<!-- 定义查问汇合元素 -->
<select id="getDeptAndEmps" resultMap="deptEmp">
select * from dept left join emp on dept.deptno = emp.deptno where dept.deptno=#{deptno}
</select>
<resultMap id="deptEmp" type="com.mashibing.bean.Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!-- 封装汇合类的元素
property:指定汇合的属性
ofType: 指定汇合中的元素类型
-->
<collection property="emps" ofType="com.mashibing.bean.Emp">
<id property="empno" column="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
</collection>
</resultMap>
</mapper>
Test
@Test
public void test09() {
// 获取数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
try {DeptDao mapper = sqlSession.getMapper(DeptDao.class);
Dept deptAndEmps = mapper.getDeptAndEmps(10);
System.out.println(deptAndEmps);
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
7、分步查问
在上述逻辑的查问中,是由咱们本人来实现 sql 语句的关联查问的,那么,咱们能让 mybatis 帮咱们实现主动的关联查问吗?
关联查问的分步
DeptDao.java
package com.mashibing.dao;
import com.mashibing.bean.Dept;
import com.mashibing.bean.Emp;
import java.util.List;
public interface DeptDao {public Dept getDeptAndEmps(Integer deptno);
public Dept getDeptAndEmpsBySimple(Integer deptno);
}
EmpDao.java
package com.mashibing.dao;
import com.mashibing.bean.Emp;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface EmpDao {Emp selectEmpAndDept(Integer empno);
Emp selectEmpAndDeptBySimple(Integer empno);
}
DeptDao.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="com.mashibing.dao.DeptDao">
<select id="getDeptAndEmpsBySimple" resultType="com.mashibing.bean.Dept">
select * from dept where deptno = #{deptno}
</select>
</mapper>
EmpDao.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="com.mashibing.dao.EmpDao">
<select id="selectEmpAndDeptBySimple" resultMap="simpleEmpAndDept">
select * from emp where empno = #{empno}
</select>
<resultMap id="simpleEmpAndDept" type="com.mashibing.bean.Emp">
<id column="empno" property="empno"></id>
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="common"></result>
<association property="dept" select="com.mashibing.dao.DeptDao.getDeptAndEmpsBySimple" column="deptno">
</association>
</resultMap>
</mapper>
Test
@Test
public void test08() {
// 获取数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
try {EmpDao mapper = sqlSession.getMapper(EmpDao.class);
// Emp emp = mapper.selectEmpAndDept(7369);
Emp emp = mapper.selectEmpAndDeptBySimple(7369);
System.out.println(emp);
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
汇合的分步查问
EmpDao.java
package com.mashibing.dao;
import com.mashibing.bean.Emp;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface EmpDao {Emp selectEmpAndDeptBySimple(Integer empno);
Emp selectEmpByStep(Integer empno);
}
DeptDao.java
package com.mashibing.dao;
import com.mashibing.bean.Dept;
import com.mashibing.bean.Emp;
import java.util.List;
public interface DeptDao {public Dept getDeptAndEmps(Integer deptno);
public Dept getDeptAndEmpsBySimple(Integer deptno);
public Dept getDeptAndEmpsByStep(Integer deptno);
}
EmpDao.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="com.mashibing.dao.EmpDao">
<select id="selectEmpByStep" resultType="com.mashibing.bean.Emp">
select * from emp where deptno = #{deptno}
</select>
</mapper>
DeptDao.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="com.mashibing.dao.DeptDao">
<select id="getDeptAndEmpsByStep" resultMap="deptEmpByStep">
select * from dept where deptno = #{deptno}
</select>
<resultMap id="deptEmpByStep" type="com.mashibing.bean.Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!-- 封装汇合类的元素
property:指定汇合的属性
ofType: 指定汇合中的元素类型
-->
<collection property="emps" ofType="com.mashibing.bean.Emp" select="com.mashibing.dao.EmpDao.selectEmpByStep" column="deptno">
</collection>
</resultMap>
</mapper>
Test
@Test
public void test09() {
// 获取数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
try {DeptDao mapper = sqlSession.getMapper(DeptDao.class);
// Dept deptAndEmps = mapper.getDeptAndEmps(10);
Dept deptAndEmpsByStep = mapper.getDeptAndEmpsByStep(10);
System.out.println(deptAndEmpsByStep);
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
8、提早查问
当咱们在进行表关联的时候,有可能在查问后果的时候不须要关联对象的属性值,那么此时能够通过提早加载来实现性能。在全局配置文件中增加如下属性
mybatis-config.xml
<settings>
<!-- 开启延时加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
如果设置了全局加载,然而心愿在某一个 sql 语句查问的时候不实用延时策略,能够增加如下属性:
<association property="dept" select="com.mashibing.dao.DeptDao.getDeptAndEmpsBySimple" column="deptno" fetchType="eager"/>
3、动静 sql
动静 SQL 是 MyBatis 的弱小个性之一。如果你应用过 JDBC 或其它相似的框架,你应该能了解依据不同条件拼接 SQL 语句有多苦楚,例如拼接时要确保不能遗记增加必要的空格,还要留神去掉列表最初一个列名的逗号。利用动静 SQL,能够彻底解脱这种苦楚。
应用动静 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的弱小的动静 SQL 语言,MyBatis 显著地晋升了这一个性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动静 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,须要花工夫理解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素品种,当初要学习的元素品种比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
1、if
EmpDao.xml
<select id="getEmpByCondition" resultType="com.mashibing.bean.Emp">
select * from emp where
<if test="empno!=null">
empno > #{empno} and
</if>
<if test="ename!=null">
ename like #{ename} and
</if>
<if test="sal!=null">
sal > #{sal}
</if>
</select>
EmpDao.java
public List<Emp> getEmpByCondition(Emp emp);
Test.java
@Test
public void test10() {SqlSession sqlSession = sqlSessionFactory.openSession();
try {EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
emp.setEmpno(6500);
emp.setEname("%E%");
emp.setSal(500.0);
List<Emp> empByCondition = mapper.getEmpByCondition(emp);
for (Emp emp1 : empByCondition) {System.out.println(emp1);
}
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
看起来测试是比拟失常的,然而大家须要留神的是如果咱们传入的参数值有缺失会怎么呢?这个时候拼接的 sql 语句就会变得有问题,例如不传参数或者失落最初一个参数,那么语句中就会多一个 where 或者 and 的关键字,因而在 mybatis 中也给出了具体的解决方案:
where 元素只会在子元素返回任何内容的状况下才插入“WHERE”子句。而且,若子句的结尾为“AND”或“OR”,where 元素也会将它们去除。
<select id="getEmpByCondition" resultType="com.mashibing.bean.Emp">
select * from emp
<where>
<if test="empno!=null">
empno > #{empno}
</if>
<if test="ename!=null">
and ename like #{ename}
</if>
<if test="sal!=null">
and sal > #{sal}
</if>
</where>
</select>
当初看起来没有什么问题了,然而咱们的条件增加到了拼接 sql 语句的前后,那么咱们该如何解决呢?
<!--
trim 截取字符串:prefix:前缀,为 sql 整体增加一个前缀
prefixOverrides: 去除整体字符串后面多余的字符
suffixOverrides: 去除前面多余的字符串
-->
<select id="getEmpByCondition" resultType="com.mashibing.bean.Emp">
select * from emp
<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
<if test="empno!=null">
empno > #{empno} and
</if>
<if test="ename!=null">
ename like #{ename} and
</if>
<if test="sal!=null">
sal > #{sal} and
</if>
</trim>
</select>
2、foreach
动静 SQL 的另一个常见应用场景是对汇合进行遍历(尤其是在构建 IN 条件语句的时候)。
<!--foreach 是对汇合进行遍历
collection="deptnos" 指定要遍历的汇合
close="" 示意以什么完结
index="" 给定一个索引值
item="" 遍历的每一个元素的值
open="" 示意以什么开始
separator="" 示意多个元素的分隔符
-->
<select id="getEmpByDeptnos" resultType="Emp">
select * from emp where deptno in
<foreach collection="deptnos" close=")" index="idx" item="deptno" open="(" separator=",">
#{deptno}
</foreach>
</select>
3、choose
有时候,咱们不想应用所有的条件,而只是想从多个条件中抉择一个应用。针对这种状况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="getEmpByConditionChoose" resultType="com.mashibing.bean.Emp">
select * from emp
<where>
<choose>
<when test="empno!=null">
empno > #{empno}
</when>
<when test="ename!=null">
ename like #{ename}
</when>
<when test="sal!=null">
sal > #{sal}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
4、set
用于动静更新语句的相似解决方案叫做 set。set 元素能够用于动静蕴含须要更新的列,疏忽其它不更新的列。
<update id="updateEmpByEmpno">
update emp
<set>
<if test="empno!=null">
empno=#{empno},
</if>
<if test="ename!=null">
ename = #{ename},
</if>
<if test="sal!=null">
sal = #{sal}
</if>
</set>
<where>
empno = #{empno}
</where>
</update>
4、缓存
MyBatis 内置了一个弱小的事务性查问缓存机制,它能够十分不便地配置和定制。为了使它更加弱小而且易于配置,咱们对 MyBatis 3 中的缓存实现进行了许多改良。
默认状况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存,只须要在你的 SQL 映射文件中增加一行:
<cache/>
当增加上该标签之后,会有如下成果:
- 映射语句文件中的所有 select 语句的后果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会应用最近起码应用算法(LRU, Least Recently Used)算法来革除不须要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新距离)。
- 缓存会保留列表或对象(无论查询方法返回哪种)的 1024 个援用。
- 缓存会被视为读 / 写缓存,这意味着获取到的对象并不是共享的,能够平安地被调用者批改,而不烦扰其余调用者或线程所做的潜在批改。
在进行配置的时候还会分为一级缓存和二级缓存:
一级缓存:线程级别的缓存,是本地缓存,sqlSession 级别的缓存
二级缓存:全局范畴的缓存,不止局限于以后会话
1、一级缓存的应用
一级缓存是 sqlsession 级别的缓存,默认是存在的。在上面的案例中,大家发现我发送了两个雷同的申请,然而 sql 语句仅仅执行了一次,那么就意味着第一次查问的时候曾经将后果进行了缓存。
@Test
public void test01() {SqlSession sqlSession = sqlSessionFactory.openSession();
try {EmpDao mapper = sqlSession.getMapper(EmpDao.class);
List<Emp> list = mapper.selectAllEmp();
for (Emp emp : list) {System.out.println(emp);
}
System.out.println("--------------------------------");
List<Emp> list2 = mapper.selectAllEmp();
for (Emp emp : list2) {System.out.println(emp);
}
} catch (Exception e) {e.printStackTrace();
} finally {sqlSession.close();
}
}
在大部分的状况下一级缓存是能够的,然而有几种非凡的状况会造成一级缓存生效:
1、一级缓存是 sqlSession 级别的缓存,如果在应用程序中只有开启了多个 sqlsession,那么会造成缓存生效
@Test
public void test02(){SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
List<Emp> list = mapper.selectAllEmp();
for (Emp emp : list) {System.out.println(emp);
}
System.out.println("================================");
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
List<Emp> list2 = mapper2.selectAllEmp();
for (Emp emp : list2) {System.out.println(emp);
}
sqlSession.close();
sqlSession2.close();}
2、在编写查问的 sql 语句的时候,肯定要留神传递的参数,如果参数不统一,那么也不会缓存后果
3、如果在发送过程中产生了数据的批改,那么后果就不会缓存
@Test
public void test03(){SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
System.out.println("================================");
empByEmpno.setEname("zhangsan");
int i = mapper.updateEmp(empByEmpno);
System.out.println(i);
System.out.println("================================");
Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession.close();}
4、在两次查问期间,手动去清空缓存,也会让缓存生效
@Test
public void test03(){SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
System.out.println("================================");
System.out.println("手动清空缓存");
sqlSession.clearCache();
System.out.println("================================");
Emp empByEmpno1 = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession.close();}
2、二级缓存
二级缓存是全局作用域缓存,默认是不开启的,须要手动进行配置。
Mybatis 提供二级缓存的接口以及实现,缓存实现的时候要求实体类实现 Serializable 接口,二级缓存在 sqlSession 敞开或提交之后才会失效。
1、缓存的应用
步骤:
1、全局配置文件中增加如下配置:
<setting name="cacheEnabled" value="true"/>
2、须要在应用二级缓存的映射文件出应用 <cache/> 标签标注
3、实体类必须要实现 Serializable 接口
@Test
public void test04(){SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
sqlSession.close();
Emp empByEmpno1 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno1);
sqlSession2.close();}
2、缓存的属性
eviction: 示意缓存回收策略,默认是 LRU
LRU:最近起码应用的,移除最长工夫不被应用的对象
FIFO:先进先出,依照对象进入缓存的程序来移除
SOFT:软援用,移除基于垃圾回收器状态和软援用规定的对象
WEAK:弱援用,更踊跃地移除基于垃圾收集器状态和弱援用规定的对象
flushInternal: 刷新距离,单位毫秒
默认状况是不设置,也就是没有刷新距离,缓存仅仅调用语句时刷新
size:援用数目,正整数
代表缓存最多能够存储多少个对象,太大容易导致内存溢出
readonly:只读,true/false
true:只读缓存,会给所有调用这返回缓存对象的雷同实例,因而这些对象不能被批改。
false:读写缓存,会返回缓存对象的拷贝(序列化实现),这种形式比拟平安,默认值
// 能够看到会去二级缓存中查找数据,而且二级缓存跟一级缓存中不会同时存在数据,因为二级缓存中的数据是在 sqlsession 敞开之后才失效的
@Test
public void test05(){SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
Emp empByEmpno2 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno2);
Emp empByEmpno3 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno3);
sqlSession2.close();}
// 缓存查问的程序是先查问二级缓存再查问一级缓存
@Test
public void test05(){SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
Emp empByEmpno = mapper.findEmpByEmpno(1111);
System.out.println(empByEmpno);
sqlSession.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
Emp empByEmpno2 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno2);
Emp empByEmpno3 = mapper2.findEmpByEmpno(1111);
System.out.println(empByEmpno3);
Emp empByEmpno4 = mapper2.findEmpByEmpno(7369);
System.out.println(empByEmpno4);
Emp empByEmpno5 = mapper2.findEmpByEmpno(7369);
System.out.println(empByEmpno5);
sqlSession2.close();}
3、二级缓存的作用范畴:
如果设置了全局的二级缓存配置,那么在应用的时候须要留神,在每一个独自的 select 语句中,能够设置将查问缓存敞开,以实现非凡的设置
1、在 setting 中设置,是配置二级缓存开启,一级缓存默认始终开启
<setting name="cacheEnabled" value="true"/>
2、select 标签的 useCache 属性:
在每一个 select 的查问中能够设置以后查问是否要应用二级缓存,只对二级缓存无效
3、sql 标签的 flushCache 属性
增删改操作默认值为 true,sql 执行之后会清空一级缓存和二级缓存,而查问操作默认是 false
4、sqlSession.clearCache()
只是用来分明一级缓存
3、整合第三方缓存
在某些状况下咱们也能够自定义实现缓存,或为其余第三方缓存计划创立适配器,来齐全笼罩缓存行为。
1、导入对应的 maven 依赖
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.0-alpha1</version>
<scope>test</scope>
</dependency>
2、导入 ehcache 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘保留门路 -->
<diskStore path="D:\ehcache" />
<defaultCache
maxElementsInMemory="1"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性阐明:l diskStore:指定数据在磁盘中的存储地位。l defaultCache:当借助 CacheManager.add("demoCache") 创立 Cache 时,EhCache 便会采纳 <defalutCache/> 指定的的管理策略
以下属性是必须的:l maxElementsInMemory - 在内存中缓存的 element 的最大数目
l maxElementsOnDisk - 在磁盘上缓存的 element 的最大数目,若是 0 示意无穷大
l eternal - 设定缓存的 elements 是否永远不过期。如果为 true,则缓存的数据始终无效,如果为 false 那么还要依据 timeToIdleSeconds,timeToLiveSeconds 判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的 element 缓存到磁盘上
以下属性是可选的:l timeToIdleSeconds - 当缓存在 EhCache 中的数据前后两次拜访的工夫超过 timeToIdleSeconds 的属性取值时,这些数据便会删除,默认值是 0, 也就是可闲置工夫无穷大
l timeToLiveSeconds - 缓存 element 的无效生命期,默认是 0., 也就是 element 存活工夫无穷大
diskSpoolBufferSizeMB 这个参数设置 DiskStore(磁盘缓存) 的缓存区大小. 默认是 30MB. 每个 Cache 都应该有本人的一个缓冲区.
l diskPersistent - 在 VM 重启的时候是否启用磁盘保留 EhCache 中的数据,默认是 false。l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行距离,默认是 120 秒。每个 120s,相应的线程会进行一次 EhCache 中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的 element 退出的时候,移除缓存中 element 的策略。默认是 LRU(最近起码应用),可选的有 LFU(最不常应用)和 FIFO(先进先出)-->
3、在 mapper 文件中增加自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>