文章目录
Mybatis11_其他
1.Mybatis批量操作
其实前面有写过 Mysql 或者 Oracle的批量操作,但是那不是真正意义上的批量操作,执行每一次 sql 都要和数据库服务交互,那样有些浪费资源,Mybatis其实是支持批量操作的,在Mybatis的主配置文件中是可以配置开启批量操作,但是那是全局开启批量操作所有的sql都是使用批量执行器,不推荐这样做。
默认的 openSession() 方法没有参数,它会创建有如下特性的。
- 会开启一个事务(也就是不自动提交)
- 连接对象会从由活动环境配置的数据源实例得到。
- 事务隔离级别将会使用驱动或数据源的默认设置。
- 预处理语句不会被复用,也不会批量处理更新。
openSession 方法的 ExecutorType 类型的参数,枚举类型:
- ExecutorType.SIMPLE:这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句。
- ExecutorType.REUSE:这个执行器类型会复用预处理语句。
- ExecutorType.BATCH:这个执行器会批量执行所有更新语句。
批量操作我们是使用MyBatis提供的 BatchExecutor 进行的,他的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次。
1.批量操作演示
<!--public Long addEmp(Employee employee); -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,email,gender,empStatus)
values(#{lastName},#{email},#{gender},#{empStatus})
</insert>
//接口
public Long addEmp(Employee employee);
//测试
@Test
public void testBatch() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
long start = System.currentTimeMillis();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 10000; i++) {
mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
}
openSession.commit();
long end = System.currentTimeMillis();
//批量:(预编译sql一次,设置参数10000次,执行一次) 执行时长:5283
//Parameters: 9075c(String), b(String), 1(String)
System.out.println("执行时长:"+(end-start));
}finally{
openSession.close();
}
}
测试结果
2.非批量操作演示
@Test
public void testBatch() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//使用不可执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession();
long start = System.currentTimeMillis();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 10000; i++) {
mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
}
openSession.commit();
long end = System.currentTimeMillis();
//非批量:(预编译sql和设置参数以及执行sql都是10000次) 执行时长:10752
System.out.println("执行时长:"+(end-start));
}finally{
openSession.close();
}
}
测试结果
3.与Spring整合执行批量操作
与Spring整合中,推荐,额外的配置一个可以专门用来执行批量操作的sqlSession,在spring配置文件中添加如下配置。
<!--配置一个可以进行批量执行的sqlSession SqlSessionTemplate已经实现了SqlSession接口 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
需要用到批量操作的时候,我们可以注入配置的这个批量SqlSession。通过他获取到mapper映射器进行操作。
@Service
public class EmployeeService {
@Autowired
//private EmployeeMapper employeeMapper;
@Autowired
private SqlSession sqlSession;//这里使用的是有批量操作的sqlSession。
public List<Employee> getEmps(){
//可以执行批量操作了
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
return employeeMapper.getEmps();
}
}
注意:
1、批量操作是在session.commit()以后才发送sql语句给数据库进行执行的。
2、如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements()方法,让其直接冲刷到数据库进行执行。
2.存储过程
MyBatis对存储过程的游标提供了一个JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中。
实际开发中,我们通常也会写一些存储过程,MyBatis也支持对存储过程的调用。
CREATE
OR REPLACE PROCEDURE hello_test ( p_start IN INT, p_end IN INT, p_count OUT INT, p_emps OUT sys_refcursor )
) AS
BEGIN
SELECT
count(*) INTO p_count fomr employees;
OPEN p_emps FOR
SELECT
*
FROM
( SELECT rownum rn, e.* FROM employees e WHERE rownum <= p_end )
WHERE
rn >= p_start;
END hello_test;
首先将数据库环境换为Oracle。
<!-- public void getPageByProcedure();
1、使用select标签定义调用存储过程
2、statementType="CALLABLE":表示要调用存储过程
3、{call procedure_name(params)}
-->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
{call hello_test(
#{start,mode=IN,jdbcType=INTEGER},
#{end,mode=IN,jdbcType=INTEGER},
#{count,mode=OUT,jdbcType=INTEGER},
#{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp}
)}
</select>
<resultMap type="com.atguigu.mybatis.bean.Employee" id="PageEmp">
<id column="EMPLOYEE_ID" property="id"/>
<result column="LAST_NAME" property="email"/>
<result column="EMAIL" property="email"/>
</resultMap>
//接口
public void getPageByProcedure(OraclePage page);
//测试
/**
* oracle分页:
* 借助rownum:行号;子查询;
* 存储过程包装分页逻辑。
*/
@Test
public void testProcedure() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
OraclePage page = new OraclePage();
page.setStart(1);
page.setEnd(5);
mapper.getPageByProcedure(page);
System.out.println("总记录数:"+page.getCount());
System.out.println("查出的数据:"+page.getEmps().size());
System.out.println("查出的数据:"+page.getEmps());
}finally{
openSession.close();
}
}
3.自定义TypeHandler处理枚举
我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。
Mybatis也有默认的枚举处理类:
步骤:
1、实现TypeHandler接口或者继承BaseTypeHandler。
2、使用@MappedTypes定义处理的java类型,使用@MappedJdbcTypes定义jdbcType类型。
3、在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理或者在全局配置TypeHandler要处理的javaType。
1.测试全局配置EnumTypeHandler
创建一个枚举类
public enum EmpStatus{
LOGIN,LOGOUT,REMOVE;
}
<!--public Long addEmp(Employee employee); -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into employee(last_name,email,gender,empStatus)
values(#{lastName},#{email},#{gender},#{empStatus})
</insert>
//接口
public Long addEmp(Employee employee);
//测试
/**
* 默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler默认使用。
* 可以改为EnumOrdinalTypeHandler。
*/
@Test
public void testEnumOrdinalTypeHandler() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper employeeMapper = openSession.getMapper(EmployeeMapper.class);
employeeMapper.addEmp(new Employee("小肖", "xiaoxiao@qq.com", "1", EmpStatus.LOGIN));
openSession.commit();
} finally {
openSession.close();
}
}
测试结果
@Test
public void test() throws IOException {
//测试一下枚举类中的两个方法代表什么意思
EmpStatus login = EmpStatus.LOGIN;
System.out.println("枚举的索引:"+login.ordinal());
System.out.println("枚举的名字:"+login.name());
}
测试结果
为什么MyBatis处理枚举类型,直接存储的是字段值,为什么不是数字呢?因为Mybatis默认使用的EnumTypeHandler处理器来处理枚举类型的。
2.测试全局配置EnumOrdinalTypeHandler
如果想要修改MyBatis对枚举类型的默认处理,需要在MyBatis的主配置文件中添加typeHandlers标签来声明使用哪个处理器。
<typeHandlers>
<!--
使用EnumOrdinalTypeHandler这个处理器来处理枚举类型为EmpStatus 如果不指定javaType全局都使用
EnumOrdinalTypeHandler这个处理器
-->
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.ginger.mybatis.bean.EmpStatus"></typeHandler>
</typeHandlers>
还是上面的一样的就是在MyBatis的主配置文件中添加了一个枚举类处理器来测试。
@Test
public void testEnumOrdinalTypeHandler() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper employeeMapper = openSession.getMapper(EmployeeMapper.class);
employeeMapper.addEmp(new Employee("小杨", "xiaoyang@qq.com", "2", EmpStatus.LOGIN));
openSession.commit();
} finally {
openSession.close();
}
}
测试结果
3.测试参数位置设置自定义TypeHandler
在实际开发中我们不希望在数据库中存储的是,枚举类型自带的值,这时候就要自定义 TypeHandler。
修改枚举类
/**
* 希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名。
*/
public enum EmpStatus {
LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
private Integer code;
private String msg;
private EmpStatus(Integer code,String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
//按照状态码返回枚举对象
public static EmpStatus getEmpStatusByCode(Integer code){
switch (code) {
case 100:
return LOGIN;
case 200:
return LOGOUT;
case 300:
return REMOVE;
default:
return LOGOUT;
}
}
}
自定义枚举类型处理器
/**
* 1、实现TypeHandler接口。或者继承BaseTypeHandler
*/
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus>{
/**
* 定义当前数据如何保存到数据库中
*/
@Override
public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
JdbcType jdbcType) throws SQLException {
System.out.println("要保存的状态码:" + parameter.getCode());
ps.setString(i, parameter.getCode().toString());
}
@Override
public EmpStatus getResult(ResultSet rs, String columnName)
throws SQLException {
//需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
int code = rs.getInt(columnName);
System.out.println("从数据库中获取的状态码:" + code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
@Override
public EmpStatus getResult(ResultSet rs, int columnIndex)
throws SQLException {
int code = rs.getInt(columnIndex);
System.out.println("从数据库中获取的状态码:" + code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
@Override
public EmpStatus getResult(CallableStatement cs, int columnIndex)
throws SQLException {
int code = cs.getInt(columnIndex);
System.out.println("从数据库中获取的状态码:" + code);
EmpStatus status = EmpStatus.getEmpStatusByCode(code);
return status;
}
}
<!--Mybatis主配置文件-->
<typeHandlers>
<!--
使用MyEnumEmpStatusTypeHandler这个处理器来处理枚举类型为EmpStatus 如果不指定javaType全局都使用
MyEnumEmpStatusTypeHandler这个处理器
-->
<typeHandler handler="com.ginger.mybatis.typehandler.MyEnumEmpStatusTypeHandler" javaType="com.ginger.mybatis.bean.EmpStatus"></typeHandler>
</typeHandlers>
<!--Mybatis映射文件-->
<!--public Long addEmp(Employee employee); -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into employee(last_name,email,gender,empStatus)
values(#{lastName},#{email},#{gender},#{empStatus})
</insert>
//接口
public Long addEmp(Employee employee);
//测试
@Test
public void testEnumOrdinalTypeHandler() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper employeeMapper = openSession.getMapper(EmployeeMapper.class);
employeeMapper.addEmp(new Employee("小刘", "xiaoliu@qq.com", "1", EmpStatus.LOGIN));
openSession.commit();
} finally {
openSession.close();
}
}
测试结果