Mybatis学习记录

MyBatis-HelloWorld

MyBatis操作数据库

1,创建MyBatis全局配置文件

-MyBatis的全局配置文件包含了影响Mybatis行为甚深的设置(settings),属性(properties)信息,数据库连接池信息等.指导着Mybatis进行工作.我们可以参考官方文件的配置文件.

2,创建SQL映射文件

-映射文件的作用就相当于定义Dao接口的实现类如何工作.这也是我们使用MyBatis时编写最多的文件.

测试

1,根据全局配置文件.利用SqlSessionFactoryBuilder创建SqlSessionFactory(含有数据源等配置信息)

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2,使用SqlSessionFactory获取sqlSession对象,一个sqlSession对象代表和数据库的一次对话,

SqlSession openSession = sqlSessionFactory.openSession();

3,使用SqlSession根据方法id进行操作(方法id是xml文件中的namespace+方法id)(sql映射文件中配置所有sql,以及sql的封装规则等,sql映射文件必须配置在全局配置文件中)

Employee employee = openSession.selectOne(
					"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
//第一个参数是sql的唯一表示,来告诉MyBatis执行那个sql.sql都是保存在sql映射文件中的.

二,HelloWorld-接口式编程(接口和sql映射文件动态映射)

 定义Mapper接口

sql映射文件的namespace属性不在随意指定,应填写Mapper接口的全类名;

id:唯一标识,使用接口式编程应该和Mapper接口中的方法名一致;

此时,可以使用SqlSession获取一个Mapper的代理对象,使用Mapper的方法进行数据库的操作

EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);

SqlSession

-SqlSession的实例不是线程安全的,因此是不能被共享的.

-SqlSession每次使用完成后需要正确的关闭,这个关闭的操作是必须的

-SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作

三,MyBatis-全局配置文件

MyBatis的全局配置文件中包含了影响MyBatis行为甚深的设置(settings)和属性(properties)信息.文档的顶级结构如下:

全局配置信息

properties属性

用来加载外部properties文件的信息

有两个属性:1,resource:引入类路径下的资源;2,url:引入网络路径或者磁盘路径下的资源.

<properties resource="dbconfig.properties"></properties>

如果属性在不只一个地方进行了配置,那么MyBatis讲按照下面的顺序来加载:

-在propertie元素体内指定的属性首先被读取.

-然后根据properties元素中的resource属性读取类路径下属性文件或根据url属性指定的路径读取属性属性文件,并覆盖已读取的同名属性.

-最后读取作为方法参数传递的属性,并覆盖已读取的同名属性.

settings设置

setting来设置每一个设置,name:设置项名,value:设置项取值

这是Mybatis中极为重要的调整设置,它们会改变MyBatis的运行时行为

例:

<settings>
	<setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- 设置下划线驼峰命名 -->
</settings>

typeAliases别名处理器

可以为我们的java类型起别名,别名的类型不区分大小写

typeAlias:为某个java类型起别名

-type:指定要起别名的类型全类名;默认别名就是类名小写

-alias:指定新的别名

<typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/>

package:为某个包下所有类批量起别名

-name:指定包名(为当前包下以及下面所有的后带包的每一个类都起了一个默认别名(类名小写))

<package name="com.atguigu.mybatis.bean"/>

在package使用的情况下,可以使用@Alias注解为某个类型指定新的别名

@Alias("emp")
public class Employee {
......}

值得注意的是,MyBatis已经为许多常见的java类型内建了相应的类型别名.他们都是大小写不敏感的,我们在起别名的时候千万不要占用已有的别名;

typeHandlers类型处理器

无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换为java类型.

日期类型的处理

JDK1.8以前一直是个头疼的问题.我们通常使用JSR310规范领导者StephenColeBourne常见的Joda-Time来操作.1.8已经实现了全部的JSR310规范了.

日期时间处理上,我们可以使用MyBatis给予JSR310(Date and Time API)编写的各种日期时间类型处理器.

MyBatis3.4以前的版本需要我们手动注册这些处理器,以后的版本都是自动注册的

typeHandler

 自定义类型处理器

我们可以重写类型处理器或创建自己的类型处理器来处理不支持或非标准的类型

步骤:

  1. 实现org.apache.ibatis.type.TypeHandler接口或者继承org.apache.ibatis.type.BaseTypeHandler
  2. 指定其映射某个JDBC类型(可选操作)
  3. 在MyBatis全局配置文件中注册

plugins插件

插件是Mybatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为.插件通过动态代理机制,可以介入四大对象的任何一个方法的执行.后面会有专门的章节我们来介绍MyBatis运行原理以及插件

Executor(update,query,flushStatuss,commit,rollback,getTransaction,close,isClosed)

ParameterHandler(getParameterObject,setParamters)

ResultSetHandler(handleResultSets,handleOutputParameters)

StatementHandler(prepare,parameterize,batch,update,query)

environments环境

  • MyBatis可以配置多种环境,比如开发,测试和生产环境需要不同的配置.
  • 每种环境使用一个environment变迁进行配置并指定唯一的标识符
  • 可以通过environments标签的default属性指定一个环境的标识符来快速的切换环境

environment-指定具体的环境

-id:指定当前环境的唯一标识

- 一个environment必须存在transactionManager和dataSource

<environments default="dev_mysql">
    <environment id="dev_mysql">
		<transactionManager type="JDBC"></transactionManager>
			<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>
	<environment id="dev_oracle">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property name="driver" value="${orcl.driver}" />
			<property name="url" value="${orcl.url}" />
			<property name="username" value="${orcl.username}" />
			<property name="password" value="${orcl.password}" />
		</dataSource>
	</environment>
</environments>

transactionManager(事务管理器)

type:事物管理器类型:

  1. JDBC:使用了JDBC的提交和回滚设置,依赖于从数据源得到的链接来管理事务范围,是JdbcTransactionFactory的别名;
  2. MANAGED:不提交或回滚一个连接,让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文).是ManangedTransactionFactory的别名.
  3. 自定义:实现TransactionFactory接口,type=全类名/别名

dataSource(数据源)

type:数据源的类型:

  • UNPOOLED:不使用连接池,UnpooledDataSourceFactory的别名
  • POOLED:使用连接池,PooledDataSourceFactory的别名
  • JNDI:在EJB或应用服务器这类容器中查找指定的数据源
  • 自定义:实现DataSourceFactory接口,词能够以数据源的获取方式

实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置

databaseIdProvider环境

Mybatis可以根据不同的数据库厂商执行不同的语句

<databaseIdProvider type="DB_VENDOR">
	<!-- 为不同的数据库厂商起别名 -->
	<property name="MySQL" value="mysql"/>
	<property name="Oracle" value="oracle"/>
	<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

type:DB_VENDOR

-使用MyBatis提供的VendorDatabaseIdProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来自定义。

-property-name:数据库厂商标识   分别有MySQL、Oracle、SQL Server 等

-property-value:为标识起一个别名,方便SQL语句使用,databaseId属性进行引用

<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id = #{id}
	</select>
	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
		databaseId="mysql">
		select * from tbl_employee where id = #{id}
	</select>
	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
		databaseId="oracle">
		select EMPLOYEE_ID id,LAST_NAME	lastName,EMAIL email 
		from employees where EMPLOYEE_ID=#{id}
	</select>

  DB_VENDOR

-会通过javajdbc中的DatabaseMetaData#getDatabaseProductName()返回的字符串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短

MyBatis匹配规则如下:

-1,如果没有配置databaseIdProvider标签,那么databaseId=null;

-2,如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否者依旧为null;

-3,如果databaseId不为null,他只会找到配置databaseId的sql语句

-4,Mybatis会加载不带databaseId属性和带有匹配当前数据库databaseId属性的所有语句。如果找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃;

mapperys 映射

mapper逐个注册SQL映射文件

<mappers>
  <!-- 使用相对于类路径的资源引用 -->
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <!-- 使用完全限定资源定位符 -->
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
   <!-- 使用映射器接口实现类的完全限定类名 -->
  <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>

-resource:引用类路径下的sql映射文件

-url:引用网络路径或者磁盘路径下的sql映射文件

-class:应用(注册)接口

--1,有sql映射文件(xml),映射文件名必须和接口名相同,并且放在与接口同一目录下;(使用注意事项)

--2,没有sql映射文件,所有sql都是利用注解卸载接口上

--推荐:比较重要的,复杂的Dao接口我们写sql映射文件

             不重要的,简单的Dao接口为了开发快速可以使用注解

或者使用批量注解

<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

package的使用和上边mapper标签使用class的使用注意事项相同;

四,MyBatis-映射文件

映射文件指导者MyBatis如何进行数据库的增删改查,有着非常重要的意义;

映射文件的配置

 insert、update、delete元素

inset

 主键生产方式

-若数据库支持自动生产主键的字段(比如MySQL和SQL Server),则可以设置userGeneratedKeys="true",然后再把keyProperty设置到目标属性上。

<insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
		useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
		insert into tbl_employee(last_name,email,gender) 
		values(#{lastName},#{email},#{gender})
	</insert>

 使用后,用于插入数据库的对象的id属性就会被MyBatis赋值;

-而对于不支持自增型主键的数据库(例如Oracle),则可以使用selectKey子元素,可以设置selectKey运行的前后顺序,如果在sql之前运行,一般查询序列的下一个值(如:SELECT SRM_SEQ.nextval FROM dual),如果在sql之后运行,一般查询序列的当前值(如:SELECT SRM_SEQ.currval FROM dual)

<insert id="addEmp" databaseId="oracle">
		<!-- 
		keyProperty:查出的主键值封装给javaBean的哪个属性
		order="BEFORE":当前sql在插入sql之前运行
			   AFTER:当前sql在插入sql之后运行
		resultType:查出的数据的返回值类型
		
		BEFORE运行顺序:
			先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
			在运行插入的sql;就可以取出id属性对应的值
		AFTER运行顺序:
			先运行插入的sql(从序列中取出新值作为id);
			再运行selectKey查询id的sql;
		 -->
		<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
			<!-- 编写查询主键的sql语句 -->
			<!-- BEFORE-->
			select EMPLOYEES_SEQ.nextval from dual 
			<!-- AFTER:
			 select EMPLOYEES_SEQ.currval from dual -->
		</selectKey>
		
		<!-- 插入时的主键是从序列中拿到的 -->
		<!-- BEFORE:-->
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->}) 
		<!-- AFTER:
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(employees_seq.nextval,#{lastName},#{email}) -->
	</insert>

 selectKey

selectKey

参数(Parameters)传递

  • 单个参数:可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理(可以用任意参数接受#{abc});
  • 多个参数:任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1.。。值就是参数的值。
  • 命名参数:当多个参数时,可以使用@Param为参数设置key值,Mybatis就会将这些参数封装进Map中,key就是我们自己指定的名字;
  • POJO:当这些参数属于我们业务POJO时,我们直接传递POJO;
  • Map:我们也可以封装多个参数为Map,直接传递;
  • Collection、List、Set、Array:MyBatis会对这些参数做特殊处理,然后封装在Map中,key:Collection(collection),如果是List还可以使用key(list);数组(array),取值:取出第一个值:#{list[0]}

参数处理

参数也可以指定一个特殊的数据类型:

#{property,javaType=int,jdbcType=NUMERIC}
#{height,javaType=double,jdbcType=NUMERIC,numericScle=2}

-javaType:通常可以从参数对象来确定,但如果null别当作值来做传递,jdbcType需要被设置(Mybatis对与null类型的映射类型默认是JdbcType的OTHER类型,这中类型MySQL能做处理,但是Oracle数据库不能做处理,得设置为NULL类型:NULL类型MySQL和Oracle都能处理。处理这种问题的两种方式:1,#{email,jdbcType=OTHER};2在全局配置文件中设置<setting name="jdbcTypeForNull" value="NULL"/>);

#{key}参数位置支持的属性:javaType、 jdbcType、 mode(存储过程)、 numericScale  resultMap、 typeHandler、 jdbcTypeName、 expression;

实际上通常被设置的是可能为空的列明指定jdbcType,其他的一般不设置

#{key}与${key}区别:

#{key}获取参数的值,预编译到SQL中,预防安全注入,安全;

${key}获取参数的值,直接拼接到SQL中,有SQL注入问题。但原生Sql支持预编译的地方可以使用。例如:

select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}

select元素

定义查询操作

Id:唯一标识符。

-用来引用这条语句,需要和接口方法名一致

parameterType:参数类型

-可以不传,MyBatis会根据TypeHandler自动判断

resultType:返回值类型

-别名或者全类名,如果返回的时集合,定义集合中元素的类型。不能和resultMap同时使用

insert,update,delete

自动映射

全局settiong设置:

-autoMappingBehavior默认时PARTIAL, 开启自动映射的功能。唯一的要求时列明和JavaBean属性名一致,如果auto MappingBehavior设置为null则会取消自动映射;

-数据库字段命名规范,POJO属性符合驼峰命名法,如:A_COLUMN-------->a_Column,可以设置mapUnderscoreToCamelCase=true;

select查询的具体情况:

1,方法返回值时List集合,对应的select标签的resultType应该是集合内部元素的类型:

<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!--resultType:如果返回的是一个集合,要写集合中元素的类型  -->
<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
	select * from tbl_employee where last_name like #{lastName}
</select>

2,方法返回的是Ma<String,Object>,当select标签设置的类型是map,返回的Map,key是数据表的列名,value是表的值;

<!--public Map<String, Object> getEmpByIdReturnMap(Integer id);  -->
<select id="getEmpByIdReturnMap" resultType="map">
 	select * from tbl_employee where id=#{id}
</select>

3,方法返回的是Map<Integer,Employee>,在方法上边使用注解@MapKey("id"),resultType是Employee,这返回的Map,key是数据表主键,value是对应的Employee实例;(@MapKey也可以对应其他的值,方法返回值的key类型要对应)

    //多条记录封装一个map:Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean
	//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
	@MapKey("lastName")
	public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
    <!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);  -->
 	<select 
        id="getEmpByLastNameLikeReturnMap"resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where last_name like #{lastName}
 	</select>

resultMap:

当Bean属性和数据表列明不对应时,有3中解决方式:

  1. 当数据库列表使用的时驼峰命名规则,则可以在中心配置文件中设置驼峰命名法
  2. 查询时给相应列名以别名,别名对应Bean的属性
  3. 使用resultMap

id&result标签:映射一个单独列的值到简单数据类型;

association标签:复杂对象映射(POJO的对象属性)

-当不使用association标签时,我们可以使用级联属性封装POJO的对象属性

     <!--
		联合查询:级联属性封装结果集
	  -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="did" property="dept.id"/>
		<result column="dept_name" property="dept.departmentName"/>
	</resultMap>

-使用association进行嵌套结果查询

    <!-- 
		使用association定义关联的单个对象的封装规则;
	 -->
	<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		
		<!--  association可以指定联合的javaBean对象
		property="dept":指定哪个属性是联合的对象
		javaType:指定这个属性对象的类型[不能省略]
		-->
		<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
			<id column="did" property="id"/>
			<result column="dept_name" property="departmentName"/>
		</association>
	</resultMap>
    <select id="getEmpAndDept" resultMap="MyDifEmp">
		SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
		d.id did,d.dept_name dept_name 
        FROM tbl_employee e,tbl_dept d
		WHERE e.d_id=d.id AND e.id=#{id}
	</select>

-使用association进行分段查询

    <!-- 使用association进行分步查询:
		1、先按照员工id查询员工信息
		2、根据查询员工信息中的d_id值去部门表查出部门信息
		3、部门设置到员工中;
	 -->
	 <!--  id  last_name  email   gender    d_id   -->
	 <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!-- association定义关联对象的封装规则
	 		select:表明当前属性是调用select指定的方法查出的结果
	 		column:指定将哪一列的值传给这个方法
	 		流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
	 	 -->
 		<association property="dept" 
	 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
	 		column="d_id">
 		</association>
	 </resultMap>

分段查询可以使用延迟加载:需要在中心配置文件中添加:

        <setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>

collection-集合类型

-嵌套结果集查询

    <!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则  -->
	<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
		<id column="did" property="id"/>
		<result column="dept_name" property="departmentName"/>
		<!-- 
			collection定义关联集合类型的属性的封装规则 
			ofType:指定集合里面元素的类型
		-->
		<collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
			<!-- 定义这个集合中元素的封装规则 -->
			<id column="eid" property="id"/>
			<result column="last_name" property="lastName"/>
			<result column="email" property="email"/>
			<result column="gender" property="gender"/>
		</collection>
	</resultMap>
	<!-- public Department getDeptByIdPlus(Integer id); -->
	<select id="getDeptByIdPlus" resultMap="MyDept">
		SELECT d.id did,d.dept_name dept_name,
				e.id eid,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>

-分段查询(也可以使用延迟加载)

    <!-- collection:分段查询 -->
	<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
		<id column="id" property="id"/>
		<id column="dept_name" property="departmentName"/>
		<collection property="emps" 
			select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
			column="{deptId=id}" fetchType="lazy"></collection>
	</resultMap>
	<!-- public Department getDeptByIdStep(Integer id); -->
	<select id="getDeptByIdStep" resultMap="MyDeptStep">
		select id,dept_name from tbl_dept where id=#{id}
	</select>
    <!-- 在对应的EmplyoeeMapper接口和xml定义相应的查询方法即可 -->
    <!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
    <select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where d_id=#{deptId}
	</select>

扩展:分部查询需要传递多个参数时,可以将多列的值封装Map传递:如colum=“{key1=column1,key2,=column2}”

association或者collection标签:fetchType:默认为lazy;如果想修改可以设置;

discriminator(鉴别器)

MyBatis可以使用鉴别器判断某列的值,然后根据某列的值改变封装行为:

    <!-- <discriminator javaType=""></discriminator>
		鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
		封装Employee:
			如果查出的是女生:就把部门信息查询出来,否则不查询;
			如果是男生,把last_name这一列的值赋值给email;
	 -->  
   <resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!--
	 		column:指定判定的列名
	 		javaType:列值对应的java类型  -->
	 	<discriminator javaType="string" column="gender">
	 		<!--女生  resultType:指定封装的结果类型;不能缺少。/resultMap-->
	 		<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
	 			<association property="dept" 
			 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
			 		column="d_id">
		 		</association>
	 		</case>
	 		<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
	 		<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
		 		<id column="id" property="id"/>
			 	<result column="last_name" property="lastName"/>
			 	<result column="last_name" property="email"/>
			 	<result column="gender" property="gender"/>
	 		</case>
	 	</discriminator>
	 </resultMap>

5,动态SQL

动态SQL时MyBatis强大特性之一。极大的简化了我们拼装SQL的操作

动态SQL元素和使用JSTL或其他类似基于xml的文本处理器相似。

MyBatis采用功能强大的基于OGNL的表达式来简化操作。

-if

-choose(when,otrherwise)

-trim(where,set)

-foreach

if:判断

    <select id="getEmpsByConditionIf" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- where -->
	 	<where>
		 	<!-- test:判断表达式(OGNL)
		 	OGNL参照PPT或者官方文档。
		 	  	 c:if  test
		 	从参数中取值进行判断
		 	
		 	遇见特殊符号应该去写转义字符:
		 	&&:
		 	-->
		 	<if test="id!=null">
		 		id=#{id}
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		and last_name like #{lastName}
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		and email=#{email}
		 	</if> 
		 	<!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	and gender=#{gender}
		 	</if>
	 	</where>
	 </select>

由于只使用if时,if标签中必须写上and或者or,这样会出现多一个and或者or的错误查询语句;<where>标签主要用途是封装查询条件,其作用是舍去标签内第一出现的and或者是or;

trim(字符串截取,可以取代where(封装查询条件)和set(封装修改信息),但一般不建议使用)

    <!--public List<Employee> getEmpsByConditionTrim(Employee employee);  -->
	 <select id="getEmpsByConditionTrim" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- 后面多出的and或者or where标签不能解决 
	 	prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
	 			prefix给拼串后的整个字符串加一个前缀 
	 	prefixOverrides="":
	 			前缀覆盖: 去掉整个字符串前面多余的字符
	 	suffix="":后缀
	 			suffix给拼串后的整个字符串加一个后缀 
	 	suffixOverrides=""
	 			后缀覆盖:去掉整个字符串后面多余的字符
	 			
	 	-->
	 	<!-- 自定义字符串的截取规则 -->
	 	<trim prefix="where" suffixOverrides="and">
	 		<if test="id!=null">
		 		id=#{id} and
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		last_name like #{lastName} and
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		email=#{email} and
		 	</if> 
		 	<!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	gender=#{gender}
		 	</if>
		 </trim>
	 </select>

 chose(when,otherwise)分支选择

    <!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
	 <select id="getEmpsByConditionChoose" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee 
	 	<where>
	 		<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
	 		<choose>
	 			<when test="id!=null">
	 				id=#{id}
	 			</when>
	 			<when test="lastName!=null">
	 				last_name like #{lastName}
	 			</when>
	 			<when test="email!=null">
	 				email = #{email}
	 			</when>
	 			<otherwise>
	 				gender = 0
	 			</otherwise>
	 		</choose>
	 	</where>
	 </select>

set设置信息(作用是舍去最后出现的",")

    <!--public void updateEmp(Employee employee);  -->
	 <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>

foreach

     <!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids);  -->
	 <select id="getEmpsByConditionForeach" resultType="com.atguigu.mybatis.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>
用in进行查询

 

    <!-- 批量保存 -->
	 <!--public void addEmps(@Param("emps")List<Employee> emps);  -->
	 <!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
	<insert id="addEmps">
	 	insert into tbl_employee(
	 		<include refid="insertColumn"></include>
	 	) 
		values
		<foreach collection="emps" item="emp" separator=",">
			(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
		</foreach>
	 </insert>

批量保存1,需要支持支持values(),(),()语法(mysql)
    <!-- 这种方式需要数据库连接属性allowMultiQueries=true;
	 	这种分号分隔多个sql可以用于其他的批量操作(删除,修改) -->
	 <insert id="addEmps">
	 	<foreach collection="emps" item="emp" separator=";">
	 		insert into tbl_employee(last_name,email,gender,d_id)
	 		values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
	 	</foreach>
	 </insert> 
需要设置数据库信息为allowMultiQueries=true,一般在数据库链接后边添加
此方法还可以用作批量修改,批量删除等等

Oracle数据库支持的批量插入

Oracle数据库不支持value(),()语法;

1,使用begin-end;所有语句都放在两者之间

        1、多个insert放在begin - end里面
	 		begin
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_001','test_001@atguigu.com');
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_002','test_002@atguigu.com');
			end;
    <insert id="addEmps" databaseId="oracle">
	 	<!-- oracle第一种批量方式 -->
	 	<foreach collection="emps" item="emp" open="begin" close="end;">
	 		insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,#{emp.lastName},#{emp.email});
	 	</foreach> 
	 </insert>

2,使用中间表方法

2、利用中间表:
			insert into employees(employee_id,last_name,email)
		       select employees_seq.nextval,lastName,email from(
		              select 'test_a_01' lastName,'test_a_e01' email from dual
		              union
		              select 'test_a_02' lastName,'test_a_e02' email from dual
		              union
		              select 'test_a_03' lastName,'test_a_e03' email from dual
		       )
    <insert id="addEmps" databaseId="oracle">
	 	<!-- oracle第二种批量方式  -->
	 	insert into employees(employee_id,last_name,email)
	 			<foreach collection="emps" item="emp" separator="union"
	 				open="select employees_seq.nextval,lastName,email from("
	 				close=")">
	 				select #{emp.lastName} lastName,#{emp.email} email from dual
	 			</foreach>
	 </insert>

MyBatis的内置参数(不只是方法传递过来的参数可以获取,MyBatis还有两个内置参数)

  1. _parameter:代表整个参数。若只有一个参数:_parameter就代表这个参数;若有两个参数,参数就会被封装成一个map,_parameter就代表这个map;
  2. _databaseId;如果配置了databaseIdProvider标签。_databaseId就是代表当前数据库的别名。
    <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  		<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<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>

bind标签

可以将OGNL表达式的值绑定到一个变量上,方便后来引用这个变量,用法见上;

sql&include 标签

  • sql:经常用于将查询的列明,或者插入用的列明抽取出来,方便以后引用
  • include用来引用已经抽取的sql
  • include还可以定义一些property,sql标签内部就能使用自定义的属性,在标签内部用${property}
    <sql id="insertColumn">
	  		<if test="_databaseId=='oracle'">
	  			employee_id,last_name,email
	  		</if>
	  		<if test="_databaseId=='mysql'">
	  			last_name,email,gender,d_id
	  		</if>
	  </sql>


    <insert id="addEmps">
	 	insert into tbl_employee(
	 		<include refid="insertColumn"></include>
	 	) 
		values
		<foreach collection="emps" item="emp" separator=",">
			(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
		</foreach>
	 </insert>

六,MyBatis-缓存机制

MyBatis系统中默认定义了两级缓存:

一级缓存和二级缓存

-默认情况下,只有一级缓存(sqlSession级别的缓存,也成为本地缓存)开启。

-耳机缓存需要手动开启,他是基于namespace级别的缓存

-为了提高扩展性。Mybatis定义了缓存接口。我们可以通过实现Cache接口来定义耳机缓存

一级缓存(本地缓存)

一级缓存是一值开启的,sqlSession级别的缓存(实际是一个Map),与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,不在查询数据库;

一级缓存失效情况(没有使用到当前一级缓存,需要从数据库读取)

  1. sqlSession不同。
  2. sqlSession相同,查询条件不同(当前一级缓存中还没有这个数据)
  3. sqlSession相同,但两次查询之间执行了增删改操作(原因是增删改查询的flush Cache默认是true,避免对当前缓存中的数据造成影响)
  4. sqlSession相同,手动清除了一级缓存(缓存清空)

二级缓存(全局缓存)

基于namespace级别的缓存,一个namespace对应一个二级缓存

工作机制:

  • 一次会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;

  • 当会话关闭或提交时,一级缓存中的数据才会被保存到耳机缓存中;新的会话查询信息就可以参照二级缓存中的内容;

  • 不同的namespace查出的数据会放在自己对应的缓存中(map):

  • 开启二级缓存后,数据首先回去二级缓存中取,取不到再去数据库查询

使用:

  1. 开启全局二级缓存配置:在全局配置文件中<seting name="cacheEnable" value="true"/>

  2. 在mapper.xml中配置使用二级缓存:<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024">

        eviction:1,LRU-最近最少使用的:移除最长时间不被使用的对象;2,FIFO-先进先出:按对象进入缓存的顺序来移除它们;3,SOFT-软引用:移除基于垃圾回收器状态和软引用规则的对象;4,WEAK-若引用:更积极地移除基于垃圾收集器状态和若引用规则地对象;5,默认使用的是LRU。flushInterval:缓存刷新间隔,即缓存多长时间清空一次,默认不清空,设置为毫秒值。readOnly:是否只读;true:只读,MyBatis认为所有从缓存中获取的数据的操作都是只读操作,不会修改数据,MyBatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,但速度快;false:非只读,MyBatis觉得获取的数据可能会别修改,MyBatis会利用序列化和反序列化的技术克隆一份新的数据。安全,速度较慢;size:缓存存放多少元素;type:自定义缓存时,引用自定义缓存的全类名。

  1. 由于获取缓存不是直接从缓存中拿,而是用序列和反序列得到缓存的备份,所以POJO需要实现序列化接口

和缓存有关的设置/属性:

  • caCheEnable=true;false:关闭缓存(二级缓存关闭)(一级缓存不会关闭)
  • 每一个select标签都有useCache="true":   false:不使用缓存(一级缓存依然使用,二级缓存不使用)
  • 每个增删改标签:flushCache="true"(一级二级缓存会清除),增删改执行完成后就会清空缓存;
  • sqlSession.clearCache(),只是清除当前session的缓存;
  • <setting name="localCaheScope"> 本地缓存的作用域:(一级缓存SESSION):当前会话的所有数据保存在会话缓存中,STATEMENT:禁用一级缓存;

整合第三方缓存:

  1. 导入第三方缓存Jar包;
  2. 导入与第三方缓存整合的适配包(在MyBatis的顶级项目)
  3. mapper.xml中使用自定义缓存<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

七,整合MyBatis-Spring

官方整合的例子:

jpetstore

整合的关键配置:

    <!--创建出SqlSessionFactory对象  -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<!-- configLocation指定全局配置文件的位置 -->
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
		<!--mapperLocations: 指定mapper文件的位置-->
		<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
        <!-- 批量别名处理 -->
        <property name="typeAliasesPackage" value="com.atguigu.bean"></property>
	</bean>
	
	<!--配置一个可以进行批量执行的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>
	
	<!-- 扫描所有的mapper接口的实现,让这些mapper能够自动注入;
	base-package:指定mapper接口的包名:现在官方的实例
	 -->
	<mybatis-spring:scan base-package="com.atguigu.mybatis.dao"/>

    <!-- 以前处理的实例,可以配置更多的信息 -->
	<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.atguigu.mybatis.dao"></property>
	</bean> -->

八,MyBatis-逆向工程

MyBatis Gneerator:简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据数据表生成对应的映射文件,接口以及bean类.支持基本的额增删改查,以及QBC风格的条件查询.但是表连接,存储过程这些复杂的sql的定义需要我们手工编写;

位置:MyBatis一级项目下的generator;

MBG的是使用

使用步骤:

-1)编写MBG的配置文件(只讲述中药的配置)

  1. jdbcConnection配置数据库连接信息;
  2. javaModelGenerator配置javaBean的生成策略;
  3. sqlMapGenerator配置sql映射文件的生成策略;
  4. javaClientGenerator配置Mapper接口的生成策略;
  5. table配置要逆向解析的数据表;1,tableName:表名;2,domainObjectName:对应的javaBean名;

-2)运行代码生成器生成代码.

注意:context标签;targetRuntime属性,当值为MyBatis3时,可以生成带条件的增删改查;当值为MyBatis3Simple是生成基本的增删改查.如果想再次生成,建议将之前生成的数据删除,避免xml向后追加内容出现的问题.

MBG配置文件

MBG配置文件

 

 

 

 

 

 

 

 

 

 

 

 

生成器代码

public static void main(String[] args) throws Exception {
    List<String> warnings = new ArrayList<String>();
    boolean overwrite = true;
    File configFile = new File("mbg.xml");
    ConfigurationParser cp = new ConfigurationParser(warnings);
    Configuration config = cp.parseConfiguration(configFile);
    DefaultShellCallback callback = new DefaultShellCallback(overwrite);
    MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
    callback, warnings);
    myBatisGenerator.generate(null);
}

生成代码QBC风格的的使用

    @Test
	public void testMyBatis3() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			//xxxExample就是封装查询条件的
			//1、查询所有
			//List<Employee> emps = mapper.selectByExample(null);
			//2、查询员工名字中有e字母的,和员工性别是1的
			//封装员工查询条件的example
			EmployeeExample example = new EmployeeExample();
			//创建一个Criteria,这个Criteria就是拼装查询条件
			//select id, last_name, email, gender, d_id from tbl_employee 
			//WHERE ( last_name like ? and gender = ? ) or email like "%e%"
			Criteria criteria = example.createCriteria();
			criteria.andLastNameLike("%e%");
			criteria.andGenderEqualTo("1");
			
			Criteria criteria2 = example.createCriteria();
			criteria2.andEmailLike("%e%");
			example.or(criteria2);
			//criteria对象内部是and的关系,之间是or的关系
			List<Employee> list = mapper.selectByExample(example);
			for (Employee employee : list) {
				System.out.println(employee.getId());
			}
			
		}finally{
			openSession.close();
		}
	}

九,MyBatis-工作原理

MyBatis工作原理

 1,构建SqlSessionFactory

构建sqlSessionFactiory

 Configuration对象中包含了全局配置文件和所有Mapperxml的所有信息,并且包含解析xml得到了MappedStatement的所有对象和Mapper接口的proxyFactoryBean用于生成Mapper接口的代理实现类(在MapperRegistry内),DefaultSqlSession包含了Configuration;

2,openSession

openSession过程

 3,getMapper

getMapper过程

 4,执行方法

执行流程1

执行流程2

执行流程总结

十、MyBatis-插件开发

MyBatis在四大队形的创建过程中,都会有插件的进行,默认情况下,MyBatis允许使用插件来拦截方法调用,包括:

-Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)

-ParameterHandler(getParameterObject,setParameters)

-RestltSetHandler(handleResultSets,handleOutputParameters)

-StatementHandler(prepare,parameterize,batch,update,query)

插件原理:

在四大对象穿件的时候

  1. 每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler);
  2. 获取到所有的Interceptor(拦截器)(插件需要实现的接口)调用interceptor.plugin(target);返回target包装后的对象
  3. 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面),我们的插件可以为四大对象创建出代理对象;代理对象就可以拦截到四大对象的每一个执行;
public Object pluginAll(Object target) {
		    for (Interceptor interceptor : interceptors) {
		      target = interceptor.plugin(target);
		    }
		    return target;
		  }

插件的顺序

插件顺序

四大对象在创建时,由插件进行包装,包装的顺序时正序的,一层一层进行包装,最外层的时最后的插件;在执行的时候,是从外到里的,因此插件的执行顺序是倒序的;

插件的编写

  1. 编写Interceptor的实现类
  2. 使用@Intercepts主键完成插件的签名
  3. 将写好的插件注册到全局配置文件中
/**
 * 完成插件签名:
 *		告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts(
		{
			@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
		})
public class MyFirstPlugin implements Interceptor{

	/**
	 * intercept:拦截:
	 * 		拦截目标对象的目标方法的执行;
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
		Object target = invocation.getTarget();
		System.out.println("当前拦截到的对象:"+target);
		//拿到:StatementHandler==>ParameterHandler===>parameterObject
		//拿到target的元数据
		MetaObject metaObject = SystemMetaObject.forObject(target);
		Object value = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println("sql语句用的参数是:"+value);
		//修改完sql语句要用的参数
		metaObject.setValue("parameterHandler.parameterObject", 11);
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	/**
	 * plugin:
	 * 		包装目标对象的:包装:为目标对象创建一个代理对象
	 */
	@Override
	public Object plugin(Object target) {
		// TODO Auto-generated method stub
		//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
		System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}

	/**
	 * setProperties:
	 * 		将插件注册时 的property属性设置进来
	 */
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		System.out.println("插件配置的信息:"+properties);
	}

}
<plugins>
		<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
		<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
	</plugins>

多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助Mybatis提供的SystemMetaObject类来惊醒获取最后一层的h以及target属性的值;

插件分离

 十一:MyBatis使用场景

PageHelper插件进行分页

PageHelper插件托管在GitHub上,我们可以对照官方文档的说明,快速的使用插件

使用步骤:

  1. 导入相关包pagehelper-xxx.jar和jsqlparser-xxx.jar
  2. 在MyBatis全局配置文件中配置分页插件
<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
	</plugins>

使用:

  1. 使用PageHelper提供的方法分页,在查询调用的代码的上句使用PageHelper.startPage(5, 1);
  2. 也可以使用更强大的PageInfo封装返回的结构;在上面的基础上进行封装;
@Test
	public void test01() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Page<Object> page = PageHelper.startPage(5, 1);
			
			List<Employee> emps = mapper.getEmps();
			//传入要连续显示多少页
			PageInfo<Employee> info = new PageInfo<>(emps, 5);
			for (Employee employee : emps) {
				System.out.println(employee);
			}
			/*System.out.println("当前页码:"+page.getPageNum());
			System.out.println("总记录数:"+page.getTotal());
			System.out.println("每页的记录数:"+page.getPageSize());
			System.out.println("总页码:"+page.getPages());*/
			///xxx
			System.out.println("当前页码:"+info.getPageNum());
			System.out.println("总记录数:"+info.getTotal());
			System.out.println("每页的记录数:"+info.getPageSize());
			System.out.println("总页码:"+info.getPages());
			System.out.println("是否第一页:"+info.isIsFirstPage());
			System.out.println("连续显示的页码:");
			int[] nums = info.getNavigatepageNums();
			for (int i = 0; i < nums.length; i++) {
				System.out.println(nums[i]);
			}
			
			
			//xxxx
		} finally {
			openSession.close();
		}
	}

批量操作

可以通过全局配置修改,但不建议,因为会改变所有的处理;

默认的openSession()方法没有设置参数,他会创建有如下特性的sqlSession

-会开启一个事务(不制动提交)

-连接对象会由活动环境配置的数据源实例得到。

-事务隔离级别竟会使用驱动或数据源的默认配置。

-预处理语句不会被服用,也不会批量处理更新。

openSession方法的ExecutorType类型的参数,枚举类型:

-ExecutorType.SIMPLE:这个执行器类型不会做特殊的事情(这是默认装配的)。它为每一个语句的执行创建一个新的预处理语句

-ExecutorType.REUSE:这个执行器类型会服用预处理语句。

-ExecutorType.BATCH:这个执行器会批量执行所用更新语句;

批处理操作我们是使用MyBatis提供的BatchExecutor进行的,它的底层就是通过JDBC积攒sql的方式进行的,只进行一次预编译,以后直接设置值。我们可以积攒够一定数量后一次执行

@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次===>执行(1次))
			//Parameters: 616c1(String), b(String), 1(String)==>4598
			//非批量:(预编译sql=设置参数=执行)==》10000    10200
			System.out.println("执行时长:"+(end-start));
		}finally{
			openSession.close();
		}
		
	}

与Spring整合中我们推荐,额外配置一个可以专名来执行批量的sqlSession

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    
    <constructor-arg name="sqlSessionFactory" ref ="sqlSessionFactoryBean"/>
    <constructor-arg name="executorType" value="BATCH"/>
</bean>
class Demo{

    @AutoWired
    private Sqlsession sqlsession; //SqlsessionTemplate实际上是sqlsession的子类

    public String method(){
        sqlsession.getMapper(Mapper.class);
    }

}

 

需要用到批量操作的使用,我们可以注入配置的这个批量SqlSession。通过他获取到Mapper进行操作。

注意:批量操作实在session.commit()以后才发送sql语句给数据库进行执行的,如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements()方法,让其直接冲刷到数据库进行执行;

存储过程

存储过程

自定义TypeHandler处理枚举

Mybatis内部有两个处理枚举类的TypeHandler

1,EnumOrdinalTypeHandler,用于把枚举和索引进行转换

2,EnumTypeHandler,用户把枚举和名称进行转换

当我们的Emum的内部属性较多时,就要使用自定义TypeHandler

步骤:

-1,实现TypeHandler接口或者继承BaseTypeHandler

-2,使用@MappedType定义处理的Java类型,使用@MappedJdbcTypes定义的jdbcType类型

-3,在自定义结果集标签或者处理参数处理的时候声明使用自定义TypeHandler进行处理或在全局配置TypeHandler要处理的JavaType

<typeHandlers>
		<!--1、配置我们自定义的TypeHandler  -->
		<typeHandler handler="com.atguigu.mybatis.typehandler.MyEnumEmpStatusTypeHandler" javaType="com.atguigu.mybatis.bean.EmpStatus"/>
		<!--2、也可以在处理某个字段的时候告诉MyBatis用什么类型处理器
				保存:#{empStatus,typeHandler=xxxx}
				查询:
					<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmp">
				 		<id column="id" property="id"/>
				 		<result column="empStatus" property="empStatus" typeHandler=""/>
				 	</resultMap>
				注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。
		  -->
	</typeHandlers>

自定以TypeHandler

@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListTypeHandler implements TypeHandler {
    private static final String LIST_SPLIT_FLAG = ",";

    public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        List<String> list = (List) parameter;
        StringBuilder stringBuilder = new StringBuilder();
        if(!CollectionUtils.isEmpty(list)){
            int j = 1;
            for(String str : list){
                stringBuilder.append(str);
                if(j++ < list.size()){
                    stringBuilder.append(",");
                }
            }
        }
        ps.setString(i,stringBuilder.toString());

    }

    public Object getResult(ResultSet rs, String columnName) throws SQLException {
        String str = rs.getString(columnName);
        return CollectionUtils.arrayToList(str.split(LIST_SPLIT_FLAG));
    }

    public Object getResult(ResultSet rs, int columnIndex) throws SQLException {
        String str = rs.getString(columnIndex);
        return CollectionUtils.arrayToList(str.split(LIST_SPLIT_FLAG));
    }

    public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String str = cs.getString(columnIndex);
        return CollectionUtils.arrayToList(str.split(LIST_SPLIT_FLAG));
    }
}

<typeHandlers>
    <typeHandler
                 handler="mybatis.typeHandler.ListTypeHandler"/>
</typeHandlers>

/**
 * 1、实现TypeHandler接口。或者继承BaseTypeHandler
 * @author lfy
 *
 */
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {

	/**
	 * 定义当前数据如何保存到数据库中
	 */
	@Override
	public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
			JdbcType jdbcType) throws SQLException {
		// TODO Auto-generated method stub
		System.out.println("要保存的状态码:"+parameter.getCode());
		ps.setString(i, parameter.getCode().toString());
	}

	@Override
	public EmpStatus getResult(ResultSet rs, String columnName)
			throws SQLException {
		// TODO Auto-generated method stub
		//需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
		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 {
		// TODO Auto-generated method stub
		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 {
		// TODO Auto-generated method stub
		int code = cs.getInt(columnIndex);
		System.out.println("从数据库中获取的状态码:"+code);
		EmpStatus status = EmpStatus.getEmpStatusByCode(code);
		return status;
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值