一、MyBatis入门 + 接口式编程(idea实现)
1.maven工程
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.项目目录
3.SQL文件
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES(1,'manster','123456'),(2,'taotao','123456'),(3,'feifei','123456');
4.javaBean
package com.manster.bean;
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
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;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", gender=" + gender + "]";
}
}
5.mybatis的全局配置文件
<?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 resource="db.properties"/>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.manster.bean"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.manster.mapper" />
</mappers>
</configuration>
6.sql映射文件
<?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.manster.mapper.EmployeeMapper">
<select id="getEmpById" resultType="employee">
select id,last_name,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
7.log4j配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration>
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
</layout>
</appender>
<logger name="java.sql">
<level value="debug"/>
</logger>
<logger name="org.apache.ibatis">
<level value="info"/>
</logger>
<root>
<level value="debug"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>
8.mapper 接口
package com.manster.mapper;
import com.manster.bean.Employee;
public interface EmployeeMapper {
Employee getEmpById(int id);
Employee getEmpAndDept(Integer id);
}
9.测试代码
public class MybatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
@Test
public void test() throws IOException {
SqlSession sqlSession = getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee empById = mapper.getEmpById(1);
System.out.println(empById);
}
}
二、全局配置文件详解
1、peoperties
mybatis可以使用properties来引入外部properties配置文件的内容
resource:引入类路径下的资源
url:引入网络或者磁盘路径下的资源
db.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456
2、settings
这是mybatis中极为重要的调整设置,他们会改变mybatis的运行时行为
<!-- 设置转驼峰
settings:所有的设置项
setting :每个设置项
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3、typeAliases 取别名
<!--别名处理器 typeAliases 为某个类型起别名 别名不区分大小写-->
<typeAliases>
<!--
typeAlias : 为某个java类型取别名
type: 指定要娶别名的类型的全类名,默认别名时类名小写employee
alias: 指定新的别名
-->
<!--<typeAlias type="com.mansterbean.Employee" alias="emp"></typeAlias>-->
<!--批量起别名
name :指定包名(为当前包已经下面所有子包的所有类起一个默认别名,类名小写)
-->
<package name="com.manster.bean"/>
</typeAliases>
4、typeHandlers_类型处理器
5、plugins_插件简介
- Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
- ParameterHandler(getParameterObject, setParameters)
- ResultSetHandler(handleResultSets,handleOutputParameters)
- StatementHandler(prepare,parameterize,batch,update,query)
6、environments
<!--
mybatis 可以配置多种环境 default指定使用某种环境
environment :配置具体的环境信息,必须有两个标签:transactionManager dataSource
id: 是环境的唯一标识
transactionManager: 事务管理器
type : JDBC|MANAGED 事务管理器的类型
dataSource: 数据源
type: UNPOOLED|POOLED|JNDI
-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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>
7、databaseIdProvider_多数据库支持
<!--
databaseIdProvider : 支持多数据库厂商
mybatis 根据数据库厂商的标识来执行不同的sql
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/> //支持mysql环境
<property name="Oracle" value="oracle"/> //支持oracle环境
</databaseIdProvider>
databaseId:告诉mybatis 这条语句是在什么环境下执行的
<select id="getEmpById" resultType="employee" databaseId="mysql">
select id,last_name,email,gender from tbl_employee where id = #{id}
</select>
8、mappers
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)
一定要注册到全局配置文件(mybatis-config.xml)中 -->
<!--
将sql映射注册到 mybatis 全局配置文件中
mapper: 注册一个sql映射
resource:引用类路径下的sql映射文件
url : 引用网络路径或者磁盘路径下的sql映射文件
class : 引用接口
1、映射文件名和接口同名,并且与接口放在同一路径下
2、没有sql映射文件,所有的sql都是利用注解写在接口上
推荐:
比较重要的,复杂的dao接口,我们来写sql映射文件
不重要,简单的sql文件我们利用注解写在接口上
package: 批量注册
映射文件名和接口同名,
并且与接口放在同一路径下
-->
<mappers>
<!--<mapper resource="EmployeeMapper.xml" />-->
<!--<mapper class="com.manster.mapper.EmployeeMapper"></mapper>-->
<package name="com.manster.mapper"/>
</mappers>
9、注意
如果出现了以下错误:
org.apache.ibatis.binding.BindingException: Invalid bound statement
(not found): com.manster.mapper.EmployeeMapper.getEmpById
而且是在idea中使用maven工程进行构建的项目,可以在pom.xml中加入以下配置来避免不编译
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
然后使 maven 进行 reload 运行即可
三、Mybatis- 映射文件
1、insert_获取自增主键的值
<!-- 增加
mybatis 也是利用 statement.getGeneratedKeys()来获取自增主键值
useGeneratedKeys ="true" 使用自增主键获取主键值策略
keyProperty : mybatis获取到主键值后,将其封装到java bean的哪个属性
-->
<insert id="addEmp" parameterType="com.manster.bean.Employee" useGeneratedKeys="true"
keyProperty="id">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
2、insert:获取非自增主键的值selectKey
<!--
oracle 不支持自增: Oracle 使用序列来模拟自增
每次插入的数据的主键是从序列中拿到的值,那么如何获取到这个值
-->
<insert id="addEmp" databaseId="oracle">
/*
keyProperty: 查出的主键值封装到javaBean的哪个属性
order = "BEFORE" 当前sql在插入sql之前运行
"AFTER" 当前sql在插入sql之后运行
resultType : 查出的数据的返回值类型
*/
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
/*编写查询主键的sql语句*/
select employees_seq.nextval from dual;
</selectKey>
/*插入的主键是从序列中拿到的*/
insert into employees(id,last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
3、mybatis参数处理
单个参数:
- mybatis不会做特殊处理,#{参数名} 取出参数
多个参数:
命名参数:
接口中的方法:
public Employee getEmpByIdAndLastName(@Param("id") int id, @Param("lastName") String lastName);
mapper.xml中
<select id="getEmpByIdAndLastName" resultType="employee">
select id,last_name,email,gender from tbl_employee
where id = #{id} and last_name =#{lastName}
</select>
POJO:
如果多个参数正好是我们业务逻辑的数据原型,可以之间传入pojo
{属性名} 取出对应的值
Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,为了方便,我们也可以传入map
{key} 取出对应的值
应用示例
public Employee GetEmp(@Param("id") Integer id,String lastName);在映射文件中怎么赋值
取值:id-->#{id/param1} last_name-->#{param2}
public Employee GetEmp( Integer id,Employee emp);在映射文件中怎么赋值
取值:id-->#{id/param1} last_name-->#{param2.lastname}
public Employee GetEmp( Integer id,@Param("em") Employee emp);在映射文件中怎么赋值
取值:id-->#{id/param1} last_name-->#{param2.lastname/em.lastname}
特别注意,如果是Collection类型(list,set)类型或者数组。会特殊处理,会把list或者数组封装进map中
如果key是Collection(collection)
是List(list)
是数组(array)
public Employee GetEmp(List<Integer> ids);在映射文件中怎么赋值
取值:id-->#{list[0]}
参数值的获取
{} :以预编译的形式,将参数设置到sql语句中,类似于jdbc的参数占位符,防止sql注入
${} : 取出的值直接拼装在sql语句中,会有安全问题
#{} 更丰富的用法
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
1、#{email,jdbcType=NULL };
2、jdbcTypeForNull=NULL
4、select 返回list和map
如果select返回list ,resultType 取得是 list 中的泛型
如果select返回map,resultType = “map”
5、select_resultMap
增加department实体类
create table tbl_dept
(
id int auto_increment
primary key,
dept_name varchar(255) null
)
-- 为employee增加外键
constraint tbl_employee_tbl_dept__fk foreign key (d_id) references tbl_dept (id)
public class Department {
private Integer id;
private String deptName;
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
private Department dept;
自定义结果映射规则:
<resultMap id="myemp" type="com.manster.bean.Employee">
<!--指定主键列的封装规则
id 定义主键,底层会有优化
column :指定哪一列
property: 指定对应的javaBean属性
-->
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
</resultMap>
<select id="getEmpById" resultMap="myemp">
select * from tbl_employee where id = #{id}
</select>
级联属性封装结果:
<resultMap id="myEmpDept" type="Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName" ></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<result column="did" property="dept.id"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="getEmpAndDept" resultMap="myEmpDept" >
select
e.id id,
e.last_name last_name,
e.gender gender,
e.email email,
d.id did,
d.dept_name dept_name
from tbl_employee e,tbl_dept d
where e.d_id = d.id
and e.d_id = #{id}
</select>
association定义关联对象封装规则(嵌套结果):
<!--
使用 association ,封装关联的单个对象
-->
<resultMap id="myEmpDept1" type="employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<!-- association 可以指定联合的javaBean对象
property : 指定哪个属性是联合的对象
javaType : 指定这个属性对象的类型
-->
<association property="dept" javaType="department">
<result column="did" property="id"></result>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="myEmpDept" >
select
e.id id,
e.last_name last_name,
e.gender gender,
e.email email,
d.id did,
d.dept_name dept_name
from tbl_employee e,tbl_dept d
where e.d_id = d.id
and e.d_id = #{id}
</select>
association分步查询(嵌套查询)
1)先准备好department对象的mapper和 xml文件
DepartmentMapper.xml
<select id="getDeptById" resultType="department">
select id,dept_name deptName from tbl_dept where id =#{id}
</select>
EmployeeMapperPlus.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.manster.mapper.EmployeeMapperPlus">
<resultMap id="myEmpDept2" type="com.manster.bean.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<!-- association 可以指定联合的javaBean对象
property : 指定哪个属性是联合的对象
select: 调用目标的方法查询当前属性的值
column: 将sql中的哪一列传入上述调用的方法
-->
<association property="dept" select="com.manster.mapper.DepartmentMapper.getDeptById" column="d_id">
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="myEmpDept2">
select
e.id id,
e.last_name last_name,
e.gender gender,
e.email email,
e.d_id
from tbl_employee e
where e.d_id = #{id}
</select>
</mapper>
分步查询&延迟加载
只有在真正用到关联对象时,才会进行第二次的分布查询
1.在mybatis-config.xml增加配置
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2.EmployeeMapperPlus.java
public interface EmployeeMapper {
Employee getEmpById(int id);
Employee getEmpAndDept(Integer id);
}
3.EmployeeMapperPlus.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.manster.mapper.EmployeeMapperPlus">
<resultMap id="myEmpByStep" type="com.manster.bean.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="gender" property="gender"></result>
<result column="email" property="email"></result>
<association property="dept" select="com.manster.mapper.DepartmentMapper.getDeptById" column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="myEmpByStep">
select * from tbl_employee where id=#{id}
</select>
</mapper>
4.Test
@Test
public void test01() throws IOException {
SqlSession sqlSession = getSqlSessionFactory().openSession();
EmployeeMapperPlus mapper = sqlSession.getMapper(EmployeeMapperPlus.class);
Employee emp = mapper.getEmpByIdStep(1);
System.out.println(emp.getLastName());
//System.out.println(emp.getDept());
}
查询与dept无关的信息
查询与dept有关的信息
collection定义关联集合封装规则
department.java中增加private List<Employee> emps;
,并添加相应的get、set方法
<?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.manster.mapper.DepartmentMapper">
<select id="getDeptById" resultType="department">
select id,dept_name deptName from tbl_dept where id =#{id}
</select>
<resultMap id="myDept" type="com.manster.bean.Department">
<id column="did" property="id"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" ofType="com.manster.bean.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="gender" property="gender"></result>
</collection>
</resultMap>
<select id="getDeptByIdPlus" resultMap="myDept">
select
d.id did,
d.dept_name dept_name,
e.id id,
e.last_name last_name,
e.email email,
e.gender gender
from tbl_dept d
left join tbl_employee e
on d.id =e.d_id
where d.id =#{id}
</select>
</mapper>
@Test
public void test02() throws IOException {
SqlSession sqlSession = getSqlSessionFactory().openSession();
DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
Department dept = mapper.getDeptByIdPlus(1);
System.out.println(dept.getEmps());
}
collection分步查询和延迟加载
DepartmentMapper.xml在接口类中也写上相应接口
<resultMap id="MyDeptStep" type="com.manster.bean.Department">
<id column="did" property="id"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" select="com.manster.mapper.EmployeeMapper.getEmpsByDeptId" column="id">
</collection>
</resultMap>
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name from tbl_dept where id=#{id}
</select>
EmployeeMapperPlus.xml在接口类中也写上相应接口
<select id="getEmpsByDeptId" resultType="com.manster.bean.Employee">
select * from tbl_employee where d_id=#{deptId}
</select>
测试
@Test
public void test02() throws IOException {
SqlSession sqlSession = getSqlSessionFactory().openSession();
DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
Department dept = mapper.getDeptByIdStep(1);
System.out.println(dept.getDeptName());
//System.out.println(dept.getEmps());
}
dept.getDeptName()
dept.getEmps()
扩展:分步查询select中方法,如果要传多列的值:
将多列的值封装成map传递
column = “{k1= column1}”
fetchType =”lazy”:表示延迟加载
-lazy :延迟加载
-eager: 立即查询
<collection property="emps" select="com.manster.mapper.EmployeeMapper.getEmpsByDeptId" column="id" fetchType="lazy">
</collection>
四、Mybatis- 动态sql
1、if标签
<select id="getEmpsByConditionIf" resultType="com.manster.bean.Employee">
select * from tbl_employee
where 1=1
<if test="id!=null">
and id = #{id}
</if>
<if test="lastName!=null and lastName !=''">
and last_name like '%${lastName}%'
</if>
<if test="email!=null">
and email like '%${email}%'
</if>
<if test="gender==0 or gender == 1">
and gender = #{gender}
</if>
</select>
2、where标签
去除动态sql中多余的and 和 or
<select id="getEmpsByConditionIf" resultType="com.manster.bean.Employee">
select * from tbl_employee
<where>
<if test="id!=null">
and id = #{id}
</if>
<if test="lastName!=null and lastName !=''">
and last_name like '%${lastName}%'
</if>
<if test="email!=null">
and email like '%${email}%'
</if>
<if test="gender==0 or gender == 1">
and gender = #{gender}
</if>
</where>
</select>
3、trim 字符串截取
<select id="getEmpsByConditionTrim" resultType="com.manster.bean.Employee">
select * from tbl_employee
<!--
后面多出的and or where 标签不能解决
trim标签体中是整个字符串拼拼接后的结果
prefix:给拼串后的字符串加一个前缀
prefixOverrides="" : 前缀覆盖 去掉整个字符串前面多余的字符串
suffixOverrides="" : 后缀覆盖 去掉整个字符串后面多余的字符串
-->
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id = #{id} and
</if>
<if test="lastName!=null and lastName !=''">
last_name like '%${lastName}%' and
</if>
<if test="email!=null">
email like '%${email}%' and
</if>
<if test="gender==0 or gender == 1">
gender = #{gender} and
</if>
</trim>
</select>
4、choose when
<select id="getEmpsByConditionChoose" resultType="com.manster.bean.Employee">
select * from tbl_employee
<where>
<choose>
<when test="id!=null">
and id = #{id}
</when>
<when test="lastName!=null and lastName !=''">
and last_name like '%${lastName}%'
</when>
<otherwise>
and gender = 1
</otherwise>
</choose>
</where>
</select>
5、set
<update id="updateEmp">
<!-- Set标签的使用 -->
update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id}
<!--
Trim:更新拼串
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id} -->
</update>
6、foreach
<select id="getEmpsByConditionForeach" resultType="com.manster.bean.Employee">
select * from tbl_employee
<!--
collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
-->
<foreach collection="ids" item="item_id" separator=","
open="where id in(" close=")">
#{item_id}
</foreach>
</select>
mysql foreach 批量保存的两种方式:
<!-- 第一种 -->
<!--public void addEmps(@Param("emps")List<Employee> emps); -->
<!--MySQL下批量保存:可以foreach遍历 mysql支持values(),(),()语法-->
<insert id="addEmps">
insert into tbl_employee(last_name,email,gender,dept_id)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
<!-- 第二种 -->
<insert id="addEmps">
<foreach collection="emps" item="emp" separator=";">
insert into tbl_employee(last_name,email,gender,dept_id)
values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
7、内置参数_parameter&_databaseId
两个内置参数:
不只是方法传递过来的参数可以被用来判断,取值。。。
mybatis默认还有两个内置参数:
_parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map;_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签。
_databaseId就是代表当前数据库的别名
<select id="getEmpsTestInnerParameter" resultType="com.manster.bean.Employee">
<if test="_databaseId=='mysql'">
select * from tbl_employee
<if test="_parameter!=null">
where last_name like #{lastName}
</if>
</if>
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter!=null">
where last_name like #{_parameter.lastName}
</if>
</if>
</select>
8、bind
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
<select id="getEmpsTestInnerParameter" resultType="com.manster.bean.Employee">
<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
<bind name="_lastName" value="'%'+lastName+'%'"/>
select * from tbl_employee
<if test="_parameter!=null">
where last_name like #{_lastName}
</if>
</select>
9、可重用sql片段
定义sql片段:
<sql id="sqlColumn">
id,last_name,email,gender
</sql>
引用sql片段:
<select id="getEmpsByConditionChoose" resultType="com.manster.bean.Employee">
select
<include refid="sqlColumn"></include>
from tbl_employee
<where>
<choose>
<when test="id!=null">
and id = #{id}
</when>
<when test="lastName!=null and lastName !=''">
and last_name like '%${lastName}%'
</when>
<otherwise>
and gender = 1
</otherwise>
</choose>
</where>
</select>
五、Mybatis- 缓存机制
mybatis包含一个非常强大的查询缓存特性,他可以非常方便的配置和定制,缓存可以极大的提高查询效率
1、默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
3、为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
1.一级缓存
一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
本地缓存不能被关闭, 但可以调用 clearCache()来清空本地缓存, 或者改变缓存的作用域.
在mybatis3.1之后, 可以配置本地缓存的作用域.在 mybatis.xml 中配置
同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
- key:hashCode+查询的SqlId+编写的sql查询语句+参数
一级缓存体验
@Test
public void testFirstCache() throws IOException {
SqlSession sqlSession = getSqlSessionFactory().openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpById(1);
System.out.println(emp);
Employee emp1 = mapper.getEmpById(1);
System.out.println(emp1);
System.out.println(emp1==emp);//true
}
失效情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
2.二级缓存
- 二级缓存(second level cache),全局作用域缓存
- 二级缓存默认不开启,需要手动配置
- MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
- 二级缓存在 SqlSession 关闭或提交之后才会生效
使用步骤
1、全局配置文件中开启二级缓存 <setting name="cacheEnabled" value="true"/>
2、需要使用二级缓存的映射文件处使用cache配置缓存<cache />
3、注意: POJO需要实现Serializable接口
二级缓存体验
//注意,一定要是一个SqlSessionFactory创建出来的sqlSession
@Test
public void testSecondCache() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
Employee emp1 = mapper1.getEmpById(1);
System.out.println(emp1);
sqlSession1.close();
Employee emp2 = mapper2.getEmpById(1);
System.out.println(emp2);
sqlSession2.close();
}
缓存相关属性
eviction=“FIFO”: 缓存回收策略:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
- 默认的是 LRU。
flushInterval: 刷新间隔,单位毫秒
- 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size: 引用数目,正整数
- 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly: 只读, true/false
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。 因此这些对象
不能被修改。这提供了很重要的性能优势。 - false:读写缓存; 会返回缓存对象的拷贝(通过序列化)。这会慢一些,
但是安全,因此默认是 false。
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。 因此这些对象
缓存有关设置
1、全局setting的cacheEnable:
– 配置二级缓存的开关。一级缓存一直是打开的。
2、 select标签的useCache属性:
– 配置这个select是否使用二级缓存。一级缓存一直是使用的
3、 sql标签的flushCache属性:
– 增删改默认flushCache=true。 sql执行以后,会同时清空一级和二级缓存。
查询默认flushCache=false。
4、 sqlSession.clearCache():
– 只是用来清除一级缓存。
5、 当在某一个作用域 (一级缓存Session/二级缓存
Namespaces) 进行了 C/U/D 操作后,默认该作用域下所
有 select 中的缓存将被clear。
六、MyBatis工作原理
mybatis的框架分层架构
1、SQLSessionFactory的初始化
configuration 封装了所有的配置文件的详细信息
整个SqlSessionFactory的初始化总结来说:
把配置文件的信息解析并保存在Configuration对象中,并返回DefaultSqlSessionFactory对象
2、openSession获取SqlSession对象
3、getMapper获取到接口的代理对象
4、查询实现
总结:
1、根据配置文件(全局配置,sql映射文件),初始化configuration对象
2、创建一个DefaultSqlSession对象,里面包含Configuration 以及 executor
3、DefaultSqlSession.getMapper(): 拿到mapper接口对应的mapperProxy
mapperProxy中有 DefaultSqlSession
4、执行增删改查方法
1)调用 DefaultSqlSession的增删改查
2)创建一个StatementHangler对象,同时也会创建一个ParameterHandler 和 ResultSetHandler
3)调用StatementHangler预编译参数和设置参数值
4)调用StatementHangler的增删改查方法
5)使用ResultHandler来封装结果
【注意】
四大对象创建的时候都有一个 interceptorChain.pluginAll(parameterHandler);