Mybatis11_其他

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();
    }
}

测试结果
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值