目录
概述
- Mybatis是一个半自动化的持久化层框架
为什么要用Mybatis?
1.原生JDBC的SQL代码夹杂在Java代码里面,维护成本高,代码耦合度高
2.Hibernate和JPA处理复杂SQL难度较大,内部自动生成的SQL开发人员无法做特殊优化,基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降;
3.对于开发人员,SQL和java编码分开,功能边界清洗;
HelloWorld
- 相关表
CREATE TABLE `emp` (
`id` int NOT NULL AUTO_INCREMENT,
`emp_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`emp_gender` char(1) COLLATE utf8mb4_bin DEFAULT NULL,
`emp_email` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
- 创建对应实体类
public class Employee {
private Integer id;
private String empName;
private String empGender;
private String empEmail;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpGender() {
return empGender;
}
public void setEmpGender(String empGender) {
this.empGender = empGender;
}
public String getEmpEmail() {
return empEmail;
}
public void setEmpEmail(String empEmail) {
this.empEmail = empEmail;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", empName='" + empName + '\'' +
", empGender='" + empGender + '\'' +
", empEmail='" + empEmail + '\'' +
'}';
}
}
- 导入相关依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--为了看看打印的sql-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 编写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>
<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?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 将写好的sqlMapper文件注册到全局配置文件中 -->
<mappers>
<mapper resource="EmployeeMapper.xml"/>
</mappers>
</configuration>
- 编写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">
<!--namespace:名称空间-->
<mapper namespace="per.xgt.mapper.EmployeeMapper">
<!-- namespace+id:唯一标识,resultType:返回值类型 -->
<select id="selectEmp" resultType="per.xgt.entity.Employee">
select * from emp where id = #{id}
</select>
</mapper>
- 测试类
public class testMybatis {
/**
* 1.根据xml配置文件创建一个sqlSessionFactory对象
* 2.获取sqlSession实例,能直接执行已经映射的sql语句
* @throws IOException
*/
@Test
public void test1() throws IOException {
// 根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();
Employee employee = sqlSession.selectOne("per.xgt.mapper.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
sqlSession.close();
}
}
- 执行结果
DEBUG 07-26 15:17:45,204 ==> Preparing: select * from emp where id = ? (BaseJdbcLogger.java:145)
DEBUG 07-26 15:17:45,230 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145)
DEBUG 07-26 15:17:45,293 <== Total: 1 (BaseJdbcLogger.java:145)
Employee{id=1, empName='null', empGender='null', empEmail='null'}
- 字段为null
查询的字段列要与类属性一致,如果不一致需要取别名
<?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="per.xgt.mapper.EmployeeMapper">
<!-- namespace+id:唯一标识,resultType:返回值类型 -->
<select id="selectEmp" resultType="per.xgt.entity.Employee">
select id,emp_name empName,emp_gender empGender,emp_email empEmail from emp where id = #{id}
</select>
</mapper>
接口式编程
- 编写接口
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
- sql映射文件的namespace为接口的全类名
namespace="per.xgt.mapper.EmployeeMapper"
- sql的id为接口的方法名
<select id="getEmpById" resultType="per.xgt.entity.Employee">
select id,emp_name empName,emp_gender empGender,emp_email empEmail from emp where id = #{id}
</select>
mybatis会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
- 测试
@Test
public void test2() throws IOException {
SqlSessionFactory sqlSessionFactory = get();
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmpById(1);
System.out.println(emp);
sqlSession.close();
}
小结
- sqlSession和connection一样都是现成非安全的,代表个数据库的一次回话,用完必须关闭,每次使用都应该去获取新的对象
- Mapper接口没有实现类,但是mybatis会为这个借口生成一个代理对象,将接口和xml进行绑定
- 两个配置文件:mybatis的全局配置文件,包含数据库连接池信息,事务管理器信息等系统运行环境;sql映射文件,包含了每一个sql语句的映射信息
mapper.xml文件位置
如果mapper文件在resource目录下,编译时可以直接找到,如果mapper文件在java目录下,则需要在pom文件中添加以下内容说明需要编译java文件下的mapper文件
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
全局配置文件
properties引入外部配置文件
<!--properties标签:引入外部properties文件
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
-->
<properties resource="jdbc.properties"></properties>
<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>
settings运行时行为设置
完整的settings配置
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
- 举例:驼峰命名
<!--settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<!--设置驼峰命名法:-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
运行结果
DEBUG 07-26 16:15:37,186 ==> Preparing: select * from emp where id = ? (BaseJdbcLogger.java:145)
DEBUG 07-26 16:15:37,213 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145)
DEBUG 07-26 16:15:37,275 <== Total: 1 (BaseJdbcLogger.java:145)
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}
typeAliases别名
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
<!--typeAliases别名处理器:可以为java类型取别名-->
<typeAliases>
<!--type:指定要取别名的全类名;alias:要取的别名,如果不写,默认为类名小写-->
<typeAlias type="per.xgt.entity.Employee" alias="emp"/>
</typeAliases>
mapper文件使用别名
<select id="getEmpById" resultType="emp">
select * from emp where id = #{id}
</select>
批量取别名
<!--typeAliases别名处理器:可以为java类型取别名-->
<typeAliases>
<!--type:指定要取别名的全类名;alias:要取的别名,如果不写,默认为类名小写-->
<typeAlias type="per.xgt.entity.Employee" alias="emp"/>
<!--package:为某个包下的所有类批量取别名
name:指定包名(为当前包以及下面所有的子包都会取别名);
别名为类名,别名不区分大小写;
如果当前包和子包下有相同名字的类,可以在类上使用注解@Alias指定别名
-->
<package name="per.xgt.entity"/>
</typeAliases>
@Alias("emp")
public class Employee
typeHandlers类型处理器
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型
plugins插件
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。 - 通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
enviroments运行环境
<!--environments:mybatis可以配置多种环境,default;指定使用哪种环境,达到快速切换环境
environment:配置一个具体的环境信息,必须有transactionManager和dataSource,id代表当前环境的唯一标识
transactionManager:事务管理器;
type:事务管理器的类型;JDBC(使用JdbcTransactionFactory管理)|MANAGED(使用ManagedTransactionFactory管理)
自定义事务管理器:实现TransactionFactory接口,type指定为全类名
dataSource:数据源
type:UNPOOLED|POOLED|JNDI
自定义数据源:实现DataSourceFactory接口,type是自定义数据源的全类名
-->
<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>
databaseIdProvider多数据库支持
<!--databaseIdProvider:支持多数据厂商
type:DB_VENDOR:得到数据库厂商的标识(驱动),mybatis就能根据数据库厂商标识来执行不同的sql;
-->
<databaseIdProvider type="DB_VENDOR">
<!--为不痛数据库厂商起别名-->
<property name="Mysql" value="mysql"/>
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
为sql指定数据库厂商
<select id="getEmpById" resultType="EMP" databaseId="mysql">
select * from emp where id = #{id}
</select>
mappers sql映射注册
<!-- mappers:将sql映射注册到全局配置中
mapper:注册一个sql映射
resource:引用类路径下的资源;
url:引用网络路径下或磁盘路径下的资源;
class:使用映射器接口实现类的完全限定类名;
方式一:有sql映射文件,映射文件名必须和就扣同名,并且放在接口同一目录下;
方式二:没有sql映射文件,所有的sql都是利用注解写在接口上;
package:将包内的映射器接口实现全部注册为映射器;
-->
class
<mappers>
<mapper resource="EmployeeMapper.xml"/>
<mapper class="per.xgt.mapper.EmployeeMapperAnnotation"/>
</mappers>
@Select("select * from emp where id = #{id}")
public Employee getEmpById(Integer id);
批量注册
<mappers>
<mapper resource="EmployeeMapper.xml"/>
<mapper class="per.xgt.mapper.EmployeeMapperAnnotation"/>
<package name="per.xgt.mapper"/>
</mappers>
package标签 name属性,映射该包下所有的sql映射文件 ,这种方式能够批量注册,并且每一对接口和xml名称必须相同
package per.xgt.mapper;
import per.xgt.entity.Employee;
/**
* @author gentao9527
* @version 1.0
* @date 2022/7/26 15:01
* @description
*/
public interface EmpMapper {
public Employee getEmpById(int id);
}
<?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="per.xgt.mapper.EmpMapper">
<select id="getEmpById" resultType="per.xgt.entity.Employee">
select * from emp where id = #{id}
</select>
</mapper>
Mapper.xml
增删改
- 增
使用insert标签,parameterType标识参数类型,返回值为影响行数
public int insertEmp(Employee employee);
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
insert into emp values(null,#{empName},#{empGender},#{empEmail})
</insert>
手动提交回滚测试代码
@Test
public void test2() throws IOException {
// 根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession实例,或渠道的SQLSession不会自动提交数据
SqlSession sqlSession = sqlSessionFactory.openSession();
Employee employee = new Employee();
employee.setEmpEmail("xgt@163.com");
employee.setEmpGender("1");
employee.setEmpName("xgt");
int insert = sqlSession.insert("per.xgt.mapper.EmployeeMapper.insertEmp", employee);
System.out.println(insert);
// 提交数据
sqlSession.commit();
// 回滚数据
sqlSession.rollback();
sqlSession.close();
}
sqlSessionFactory.openSession(true);里面参数true|false代表是否自动提交
如果有自增ID 新增执行SQL后不提交或者回滚,自增ID会有id不连续的问题
- 删除和修改
删除和修改与新增类似,只是标签不同
删除:
修改: - 返回值问题
在增删改时,返回值可以为boolean类型(只要被影响行超过0,就会返回true),或者Integer或者Long类型(返回具体影响行数);
insert获取自增主键ID
支持自增主键的数据库如:Mysql
useGeneratedKeys:使用自增主键获取主键策略
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值后,将封装给bean的哪个属性;
<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" useGeneratedKeys="true" keyProperty="id">
insert into emp values(null,#{empName},#{empGender},#{empEmail})
</insert>
@Test
public void test3() throws IOException {
// 根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession实例,或渠道的SQLSession不会自动提交数据
SqlSession sqlSession = sqlSessionFactory.openSession(true);
Employee employee = new Employee();
employee.setEmpEmail("wp@163.com");
employee.setEmpGender("1");
employee.setEmpName("wp");
int insert = sqlSession.insert("per.xgt.mapper.EmployeeMapper.insertEmpAndGetKey", employee);
System.out.println(employee);
sqlSession.close();
}
获取到的主键ID会自动封装到bean的主键属性
DEBUG 07-27 10:13:56,288 ==> Preparing: insert into emp values(null,?,?,?) (BaseJdbcLogger.java:145)
DEBUG 07-27 10:13:56,316 ==> Parameters: wp(String), 1(String), wp@163.com(String) (BaseJdbcLogger.java:145)
DEBUG 07-27 10:13:56,403 <== Updates: 1 (BaseJdbcLogger.java:145)
Employee{id=7, empName='wp', empGender='1', empEmail='wp@163.com'}
不支持自增主键的数据库实现如:Oracle
Oracle不支持自增自增,Oracle可以使用序列模拟自增;每次插入数据主键是从序列中拿到的值;
- 拿到下一个序列值
select SEQ.nextval from dual;
- 拿到当前序列值
select SEQ.currval from dual;
- mybatis获取
<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" databaseId="oracle">
/*插入时的主键是从序列中拿到的
keyProperty:查询出的主键封装给bean的哪个属性
order:before当前sql在插入sql之前运行,after当前sql在插入之后运行
resultType:查处的数据的返回值类型
*/
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select EMPLOYEE_SEQ.NEXTVAL FROM DUAL
</selectKey>
insert into emp values(#{id},#{empName},#{empGender},#{empEmail})
</insert>
- 或者sql直接获取序列插入
<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" databaseId="oracle">
/*插入时的主键是从序列中拿到的
keyProperty:查询出的主键封装给bean的哪个属性
order:before当前sql在插入sql之前运行,after当前sql在插入之后运行
resultType:查处的数据的返回值类型
*/
<selectKey keyProperty="id" order="AFTER" resultType="Integer">
select EMPLOYEE_SEQ.currval FROM DUAL
</selectKey>
insert into emp values(EMPLOYEE_SEQ.NEXTVAL,#{empName},#{empGender},#{empEmail})
</insert>
参数处理
- 单个参数
mybatis不会做特殊处理,#{参数名}直接取出; - 多个参数
mybatis会做特殊处理,多个参数会被封装成一个map,key为param1,param2…,#{}就是从map中获取指定的key值。
取值可以根据参数索引获取
public Employee getEmp(int id,String name);
<select id="getEmp" resultType="per.xgt.entity.Employee">
select * from emp where id = #{param1} and empName = #{param2}
</select>
可以使用全局配置,useActualParamName(jdk1.8),name=参数名
- 命名参数
明确指定封装参数时的key
public Employee getEmp(@Param("id") int id,
@Param("name")String name);
<select id="getEmp" resultType="per.xgt.entity.Employee">
select * from emp where id = #{id} and empName = #{name}
</select>
- POJO参数
如果多个参数,可以封装成一个实体类传参,#{属性名},直接取出传入的pojo属性值。
public int insertEmp(Employee employee);
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
insert into emp values(null,#{empName},#{empGender},#{empEmail})
</insert>
- Map
如果多个参数不是业务模型中的数据,没有对应的POJO,为了方便也可以传入map。#{key},直接取出传入的map的对应value值。
public int insertEmpByMap(Map<String,Object> map);
<insert id="insertEmpByMap" parameterType="java.util.Map">
insert into emp values(null,#{empName},#{empGender},#{empEmail})
</insert>
$和#
#{}:可以获取mao中的值,或者pojo对象属性的值;
${}:效果和#{}一样
- ${}
DEBUG 07-27 11:21:33,179 ==> Preparing: select * from emp where id = 1 (BaseJdbcLogger.java:145)
DEBUG 07-27 11:21:33,210 ==> Parameters: (BaseJdbcLogger.java:145)
DEBUG 07-27 11:21:33,273 <== Total: 1 (BaseJdbcLogger.java:145)
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}
- #{}
DEBUG 07-27 11:22:34,662 ==> Preparing: select * from emp where id = ? (BaseJdbcLogger.java:145)
DEBUG 07-27 11:22:34,687 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:145)
DEBUG 07-27 11:22:34,775 <== Total: 1 (BaseJdbcLogger.java:145)
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}
- 区别
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement,防止sql注入;
${}:取出的值直接拼装在sql语句中,会有安全问题; - 注意
》#{}会在参数上会带着单引号,而单引号会被识别为字符串;
》大多数情况下,我们取参数的值都应该使用#{};
》原生JDBC不支持占位符的地方,就可以使用${}取值
#{}相关规则
- 规则参数的一些规则:
》javaType:java
》jdbcType:通常需要在某种特定的条件下被设置;在数据为null的时候,有些数据库可能不能识别mybatis堆null的默认处理jdbc other,比如Oracle;
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
insert into emp values(null,#{empName},#{empGender,jdbcType=NULL},#{empEmail})
</insert>
还可以全局配置:jdbcTypeForNull=NULL;
》mode:存储过程
》numericScale:
》resultMap:
》typeHandler:
》jdbcTypeName:
返回处理
返回List
public List<Employee> getAllEmployee();
<select id="getAllEmployee" resultType="per.xgt.entity.Employee">
select * from emp
</select>
返回map
- 单条记录封装map
返回一条记录的map,key就是列名,值就是value;
public Map<String,Object> getOneEmployeeByMap(int id);
<select id="getOneEmployeeByMap" resultType="java.util.Map">
select * from emp where id = #{id}
</select>
- 多条记录封装成一个map,key是id,value是一个实体类
在接口方法声明上用@MapKey(“empName”)注解告诉mybatis封装时map的key用哪个属性;
@MapKey("empName")
public Map<String,Object> getOneEmployeeByMaps();
<select id="getOneEmployeeByMaps" resultType="per.xgt.entity.Employee">
select * from emp
</select>
resultMap自定义结果映射
- 自动映射:
全局设置:autoMapptinBenavior默认是PARTIAL,开启自动映射,要求是列名和bean的属性名一致,设置为null则会取消自动映射; - resultType和resultMap只能二选一
<!--自定义某个javaBean的封装规则
type:自定义规则的Java类型
id:方便引用
-->
<resultMap id="myEmp" type="per.xgt.entity.Employee">
<!--指定主键列的封装规则
column:指定哪一列
property:指定对应的javaBean属性名
-->
<id column="id" property="id"/>
<!--定义普通列规则
其他不指定的列会自动封装
-->
<result column="empName" property="emp_name"/>
<result column="empGender" property="emp_gender"/>
<result column="empEmail" property="emp_email"/>
</resultMap>
<select id="findOneById" resultMap="myEmp">
select * from emp where id = #{id}
</select>
关联查询-单个对象的级联属性
public class Employee {
private Integer id;
private String empName;
private String empGender;
private String empEmail;
private Department dept;
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", empName='" + empName + '\'' +
", empGender='" + empGender + '\'' +
", empEmail='" + empEmail + '\'' +
", dept=" + dept +
'}';
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpGender() {
return empGender;
}
public void setEmpGender(String empGender) {
this.empGender = empGender;
}
public String getEmpEmail() {
return empEmail;
}
public void setEmpEmail(String empEmail) {
this.empEmail = empEmail;
}
}
public class Department {
private Integer id;
private String deptName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", deptName='" + deptName + '\'' +
'}';
}
}
public Employee findOneAndDeptById(int id);
使用属性.属性赋值
<!--级联属性封装:
直接属性.属性
-->
<resultMap id="findOneAndDeptByIdMap" type="per.xgt.entity.Employee">
<id column="id" property="id"/>
<result column="empName" property="empName"/>
<result column="empGender" property="empGender"/>
<result column="empEmail" property="empEmail"/>
<result column="dId" property="dept.id"/>
<result column="deptName" property="dept.deptName"/>
</resultMap>
<select id="findOneAndDeptById" resultMap="findOneAndDeptByIdMap">
SELECT e.id id,
e.emp_gender empGender,
e.emp_name empName,
e.emp_email empEmail,
d.id dId,
d.dept_name deptName
from emp e,dept d
where e.dept_id = d.id
and e.id = #{id};
</select>
association指定联合单个对象
<resultMap id="findOneAndDeptByIdMapAssociation" type="per.xgt.entity.Employee">
<id column="id" property="id"/>
<result column="empName" property="empName"/>
<result column="empGender" property="empGender"/>
<result column="empEmail" property="empEmail"/>
<!--association指定联合的javaBean对象
property:指定哪个属性是联合对象
javaType:指定这个属性对象的类型
-->
<association property="dept" javaType="per.xgt.entity.Department">
<id column="dId" property="id"/>
<result column="deptName" property="deptName"/>
</association>
</resultMap>
<select id="findOneAndDeptById" resultMap="findOneAndDeptByIdMapAssociation">
SELECT e.id id,
e.emp_gender empGender,
e.emp_name empName,
e.emp_email empEmail,
d.id dId,
d.dept_name deptName
from emp e,dept d
where e.dept_id = d.id
and e.id = #{id};
</select>
association分布查询
<!--分布查询:查询员工,按照员工查询部门,将部门封装到员工中-->
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Employee">
<id column="id" property="id"/>
<result column="empName" property="emp_name"/>
<result column="empGender" property="emp_gender"/>
<result column="empEmail" property="emp_email"/>
<!--定义关联对象封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
-->
<association property="dept" select="per.xgt.mapper.DepartmentMapper.findOneById" column="dept_id">
</association>
</resultMap>
<select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
select * from emp where id = #{id}
</select>
- 延迟加载
全局配置:
》lazyLoadingEnable=true|false;开启时,只有真正使用数据的时候才发起查询,不用的时候不查询关联的数据
》aggressiveLazyLoading=true|false;开启会一次性全部加载
collection关联集合封装
public class Department {
private Integer id;
private String deptName;
private List<Employee> emps;
public List<Employee> getEmps() {
return emps;
}
public void setEmps(List<Employee> emps) {
this.emps = emps;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
public Department findEmployeesByDept(Integer id);
<resultMap id="findEmployeesByDeptMap" type="per.xgt.entity.Department">
<id column="id" property="id"/>
<result column="deptName" property="deptName"/>
<!--collection定义集合类型的属性的封装规则
ofType:指定集合里面元素的类型
-->
<collection property="emps" ofType="per.xgt.entity.Employee">
<!--定义集合中元素封装规则-->
<id column="empId" property="id"/>
<result column="empName" property="empName"/>
<result column="empGender" property="empGender"/>
<result column="empEmail" property="empEmail"/>
</collection>
</resultMap>
<select id="findEmployeesByDept" resultMap="findEmployeesByDeptMap">
select d.id id,
d.dept_name deptName,
e.id empId,
e.emp_name empName,
e.emp_gender empGender,
e.emp_email empEmail
from dept d
left join emp e
on d.id = e.dept_id
where d.id = #{id}
</select>
分步查询
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
<id column="id" property="id"/>
<result column="deptName" property="deptName"/>
<collection property="emps" select="per.xgt.mapper.EmployeeMapper.findAllByDeptId" column="id">
</collection>
</resultMap>
<select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
select * from dept where id = #{id}
</select>
分步查询传递多列参数
将多列的值封装map传递
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
<id column="id" property="id"/>
<result column="deptName" property="deptName"/>
<collection property="emps"
select="per.xgt.mapper.EmployeeMapper.findAllByDeptId"
column="{id=id}">
</collection>
</resultMap>
<select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
select * from dept where id = #{id}
</select>
column=“{key1=column1,key2=column2}”
fetchType:定义加载时机eager|lazy
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
<id column="id" property="id"/>
<result column="deptName" property="deptName"/>
<collection property="emps"
select="per.xgt.mapper.EmployeeMapper.findAllByDeptId"
column="{id=id}" fetchType="lazy">
</collection>
</resultMap>
discriminator鉴别器
判断某列的值,改变封装行为;
<resultMap id="findOneByStepAndIdMapMore" type="per.xgt.entity.Employee">
<id column="id" property="id"/>
<result column="empName" property="emp_name"/>
<result column="empGender" property="emp_gender"/>
<result column="empEmail" property="emp_email"/>
<!--鉴别器
column:指定判定的列名
-->
<discriminator javaType="string" column="emp_gender">
<!--resultType:指定封装的结果类型-->
<case value="0" resultType="per.xgt.entity.Employee">
<association property="dept" select="per.xgt.mapper.DepartmentMapper.findOneById" column="dept_id">
</association>
</case>
<case value="1" resultType="per.xgt.entity.Employee">
<id column="id" property="id"/>
<result column="empName" property="emp_name"/>
<result column="empGender" property="emp_gender"/>
<result column="empEmail" property="emp_name"/>
</case>
</discriminator>
</resultMap>
<select id="findOneByStepAndId" resultMap="findOneByStepAndIdMapMore">
select * from emp where id = #{id}
</select>
动态SQL
if
<select id="findEmpsByIf" parameterType="per.xgt.entity.Employee" resultType="per.xgt.entity.Employee">
select *
from emp
where 1 = 1
/*if test,判断表达式 OGNL
从参数中取值进行判断;
遇见特殊符号比如双引号,应该写转义字符;
*/
<if test="id != null">
and id = #{id}
</if>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
/*OGNL会进行字符串与数字的转换判断*/
<if test="empGender == '0' or empGender == '1'">
and emp_gender = #{empGender}
</if>
<if test="empEmail != null and empEmail.trim() != ''">
and emp_email = #{empEmail}
</if>
</select>
注意:
如果id == null,那么where后面直接就是 and emp_name = #{empName},所以需要在后面添上1=1;
where
解决上面if标签的问题,可以使用where标签来将所有的查询条件包括在内,会自动去掉第一个and或者or
<select id="findEmpsByIf" parameterType="per.xgt.entity.Employee" resultType="per.xgt.entity.Employee">
select *
from emp
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="empGender == '0' or empGender == '1'">
and emp_gender = #{empGender}
</if>
<if test="empEmail != null and empEmail.trim() != ''">
and emp_email = #{empEmail}
</if>
</where>
</select>
trim
**自定义字符串截取:**可以解决前面where标签的问题
<select id="findEmpsByTrim" resultType="per.xgt.entity.Employee">
select *
from emp
/*后面多出的and或者or where标签不能解决
prefix="":前缀,给整个拼接后的字符串添加一个前缀
prefixOverrides="":前缀覆盖,去掉整个前面多余的字符
suffix=“”:还给整个拼接后的字符串添加后缀
suffixOverrides="":后缀覆盖
*/
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="empGender == '0' or empGender == '1'">
emp_gender = #{empGender} and
</if>
<if test="empEmail != null and empEmail.trim() != ''">
emp_email = #{empEmail}
</if>
</trim>
</select>
choose
分支选择:
<select id="findEmpsByChoose" resultType="per.xgt.entity.Employee">
select * from emp
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="empName != null">
emp_name = #{empName}
</when>
<when test="empGender != null">
emp_gender = #{empGender}
</when>
<otherwise>
where 1 = 1
</otherwise>
</choose>
</where>
</select>
set
封装修改条件
问题:根据条件更新的时候,有可能会多出末尾的逗号","
<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
update emp
set
<if test="empName != null">
emp_name = #{empName} ,
</if>
<if test="empGender != null">
emp_gender = #{empGender} ,
</if>
<if test="empEmail != null">
emp_email = #{empEmail}
</if>
where id = #{id}
</update>
解决:更新操作放在set标签
<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
update emp
<set>
<if test="empName != null">
emp_name = #{empName} ,
</if>
<if test="empGender != null">
emp_gender = #{empGender} ,
</if>
<if test="empEmail != null">
emp_email = #{empEmail}
</if>
</set>
where id = #{id}
</update>
也可以用trim解决问题:
<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
update emp
<trim prefix="set" suffixOverrides=",">
<if test="empName != null">
emp_name = #{empName} ,
</if>
<if test="empGender != null">
emp_gender = #{empGender} ,
</if>
<if test="empEmail != null">
emp_email = #{empEmail}
</if>
</trim>
where id = #{id}
</update>
但是如果所有条件都不满足,还是会报错;
- 推荐使用set
foreach
遍历集合
查询
public List<Employee> findEmpsByList(List<Integer> list);
<!--select * from emp where id in (1,2,3)-->
<select id="findEmpsByList" resultType="per.xgt.entity.Employee">
select * from emp where id in
/*collection:指定要遍历的集合
list类型的参数会特殊处理封装在map中:map的key就是list
item:遍历出的元素赋值给指定的变量;
separator:每个元素之间的分隔符;
open:遍历的所有结果拼接出一个开始的字符;
open:遍历的所有结果拼接出一个结束的字符;
index:遍历list的时候是索引,遍历map的时候是map的key,item就是map的value
*/
<foreach collection="list" item="id" separator="," open="(" close=")" index="">
#{id}
</foreach>
</select>
批量插入
mysql
- 方法一
public int addEmps(List<Employee> emps);
<insert id="addEmps" parameterType="java.util.List">
insert into emp (emp_name,emp_gender,emp_email,dept_id) values
<foreach collection="list" item="emp" separator=",">
(#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
</foreach>
</insert>
注意点
1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可
也可以指定参数名
public int addEmps(@Param("emps") List<Employee> emps);
<insert id="addEmps" parameterType="java.util.List">
insert into emp (emp_name,emp_gender,emp_email,dept_id) values
<foreach collection="emps" item="emp" separator=",">
(#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
</foreach>
</insert>
- 方法二
一次执行多条sql,需要先允许使用“;”分割多条SQL来执行:allowMultiQueries
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
<insert id="addEmps" parameterType="java.util.List">
<foreach collection="emps" item="emp" separator=";">
insert into emp (emp_name,emp_gender,emp_email,dept_id) values
(#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
</foreach>
</insert>
Oracle
- 方法一
多个insert放在begin end内;
<insert id="addEmpsOracle1" parameterType="java.util.List">
<foreach collection="emps" item="emp" open="begin" close="end;">
insert into emp (id,emp_name,emp_gender,emp_email,dept_id) values
(employee_seq,nextval,#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id});
</foreach>
</insert>
- 方法二
利用中间表
<insert id="addEmpsOracle2" parameterType="java.util.List">
insert into emp (id,emp_name,emp_gender,emp_email,dept_id)
select employees_seq.nextval,emp_name,emp_gender,emp_email,dept_id
from
<foreach collection="emps" item="emp" separator="union" open="(" close=")">
select #{emp.empName} emp_name,
#{emp.empGender} emp_gender,
#{empempEmail} emp_email,
#{emp.dept.id} dept_id
from dual
</foreach>
</insert>
内置参数
不只是方法参数传递的参数可以判断,mybatis默认还有两个内置参数;
- _parameter:代表整个参数
单个参数: _parameter就是这个参数;
多个参数:参数会被封装为一个map, _parameter就代表这个map; - _databaseId:如果配置了databaseIdProvider标签, _databaseId就是代表当前数据库的别名;
bind
可以将OGNL表达式的值绑定到一个变量中,方便后面引用
<select id="getEmpByLikeBind" resultType="per.xgt.entity.Employee">
<bind name="_name" value="'%' + empName + '%'"/>
select *
from emp
where id = #{value}
and emp_name like #{_name}
</select>
sql与include
sql:抽取可重用的sql片段
include:引用一斤股抽取的sql
- include可以自定义一些property,sql便签内部就能使用这些自定义的属性property,使用${name}
<sql id="insertColumn">
<if test="_databaseId == 'mysql'">
(id,emp_name,emp_gender,emp_email,${aa})
</if>
<if test="_databaseId == 'oracle'">
(emp_name,emp_gender,emp_email)
</if>
</sql>
<insert id="addOne">
insert into emp
/*引用抽出的sql片段*/
<include refid="insertColumn">
<property name="aa" value="vvv"/>
</include>
<if test="_databaseId == 'mysql'">
values (null,'tom','0','tom@163.com')
</if>
<if test="_databaseId == 'oracle'">
values ('tom','0','tom@163.com')
</if>
</insert>
缓存
mybatis系统中默认定义了两级缓存
- 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启;
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,mybatis定义了缓存接口Cache,可以实现Cache接口自定义二级缓存
一级缓存
本地缓存:与数据库同一次会话期间查询到的数据会被放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据;
缓存失效的情况
- SqlSession不同;
- SqlSession相同,查询条件不同且当前一级缓存还没有这个数据;
- SqlSession相同,多次查询期间执行了增删改操作;
- SqlSession相同,手动清除了一级缓存;
sqlSession.clearCache();
二级缓存
全局缓存:基于namespace级别的缓存,一个namespace对应一个二级缓存;
机制
》一个会话查询一条数据,这条数据就会被放在当前会话的一级缓存中;
》如果会话关闭,一级缓存的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存的内容;
》如果SqlSesison使用了多个namespace,不同namespace查询出的数据会放到自己对应的缓存中;
- 二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的
public class Employee implements Serializable
配置
- 开启全局配置
<!--开启全局二级缓存-->
<setting name="cacheEnabled" value="true"/>
- 在mapper.xml中配置使用二级缓存
<!--
eviction:回收策略:默认是 LRU 最近最少回收策略
LRU - 最近最少回收,移除最长时间不被使用的对象
FIFO - 先进先出,按照缓存进入的顺序来移除它们
SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
flushInterval:缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
readOnly:是否只读;true|false,MyBatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis觉得数据可能会被修改
size:缓存存放多少个元素
type:指定自定义缓存的全类名(实现Cache 接口即可)
blocking:若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
-->
<cache eviction="LRU" flushInterval="1000*60*5" readOnly="false" size="1024" blocking=""></cache>
查询的数据会从二级缓存中获取,但是查出的数据会被默认先放在一级缓存中,只有会话提交或者关闭后,一级缓存的数据才会转移到二级缓存
- select标签里面的useCache标签,也可以启用二级缓存
<select id="getEmpById" resultType="per.xgt.entity.Employee" useCache="true">
select *
from emp
where id = #{value}
</select>
-
查询和更新操作的标签:flushCache=“true”,执行完后会清除一级和二级缓存;
-
全局配置:mybatis3.3后->localCacheScope:本地缓存作用域,SESSION|STATEMENT,STATEMENT代表不共享当前数据,可以禁用掉一级缓存;
获取数据顺序
二级缓存》一级缓存》数据库
三方缓存
- 引入依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
- 编写ehcache配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<!--
maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
eternal:设置对象是否为永久的,true表示永不过期,此时将忽略
timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
</ehcache>
- mapper文件中启用ehcache
<!--自定义缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
</cache>
- 其他mapper可以声明和哪个namespace使用的缓存一样
<!--指定和哪个名称空间下的缓存一样-->
<cache-ref namespace="per.xgt.mapper.DepartmentMapper"/>
与Spring整合
导入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>per.xgt</groupId>
<artifactId>mybatis-04-ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mybatis-04-ssm Maven Webapp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.1</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-spec -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
数据库连接properties配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
web.xml配置文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>SSM</display-name>
<!--加载spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 启动Web容器时,自动装配ApplicationContext的配置信息 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--SpringMVC 配置 前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--当值为0或者大于0时,代表容器启动时加载该servlet。
正数的值越小,启动时加载该servlet的优先级越高。
如果为负数,则容器启动时不会加载该servlet,只有该servlet被选择时才会加载-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
SpringMVC配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--只扫描Controller注解,只是控制网站跳转逻辑-->
<context:component-scan base-package="per.xgt" use-default-filters="true">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--试图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!--后缀-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--自动处理静态资源,将mvc不能处理的资源交给Tomcat容器-->
<mvc:default-servlet-handler/>
<!--开启注解,能支持springmvc更高级的功能-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<!--spring希望管理所有的业务组件,除了控制器其他的都管理-->
<context:component-scan base-package="per.xgt">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入外部配置文件properties-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--spring控制业务逻辑,数据源,事务,aop等...-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--spring事务管理,开启基于注解的事务-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--整合mybatis
1.spring来管理所有组件,包括mapper.xml
2.spring来管理事务,声明式事务
-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--指定mybatis的全局配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--指定mapper文件的位置-->
<property name="mapperLocations" value="classpath*:per/xgt/dao/*.xml"></property>
</bean>
<!--扫描所有的mapper接口的实现,让这些emapper能够自动注入-->
<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">-->
<!-- <property name="basePackage" value="per.xgt.dao"></property>-->
<!-- </bean>-->
<!--效果同上-->
<mybatis-spring:scan base-package="per.xgt.dao" />
<!--开启注解驱动-->
<tx:annotation-driven />
</beans>
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>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
类和方法
- POJO
public class Employee {
private Integer id;
private String empName;
private String empGender;
private String empEmail;
public Employee(Integer id, String empName, String empGender, String empEmail) {
this.id = id;
this.empName = empName;
this.empGender = empGender;
this.empEmail = empEmail;
}
public Employee() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpGender() {
return empGender;
}
public void setEmpGender(String empGender) {
this.empGender = empGender;
}
public String getEmpEmail() {
return empEmail;
}
public void setEmpEmail(String empEmail) {
this.empEmail = empEmail;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", empName='" + empName + '\'' +
", empGender='" + empGender + '\'' +
", empEmail='" + empEmail + '\'' +
'}';
}
}
- DAO
public interface EmployeeMapper {
public Employee findOneById(Integer id);
public List<Employee> findAllEmps();
}
- Mapper.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="per.xgt.dao.EmployeeMapper">
<select id="findOneById" resultType="per.xgt.entity.Employee">
select * from emp where id = #{id}
</select>
<select id="findAllEmps" resultType="per.xgt.entity.Employee">
select * from emp
</select>
</mapper>
- Service接口
public interface EmployeeService {
public List<Employee> findAllEmps();
}
- ServiceImpl
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Override
public List<Employee> findAllEmps() {
List<Employee> emps = employeeMapper.findAllEmps();
return emps;
}
}
- Controller
@Controller
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@RequestMapping("findAllEmps")
public String emps(Map<String,Object> map){
List<Employee> emps = employeeService.findAllEmps();
for (Employee emp : emps) {
System.out.println(emp);
}
map.put("emps", emps);
return "list";
}
}
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>Hello World!</h2>
<a href="findAllEmps">查询所有员工</a>
</body>
</html>
- list.jsp
<%--
Created by IntelliJ IDEA.
User: Valen
Date: 2022/7/28
Time: 16:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table>
<tr>
<td>id</td>
<td>empName</td>
<td>empGender</td>
<td>empEmail</td>
</tr>
<c:forEach items="${emps}" var="emp">
<tr>
<td>${emp.id}</td>
<td>${emp.empName}</td>
<td>${emp.empGender}</td>
<td>${emp.empEmail}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
插件
所有mybatis对象,都会通过下面封装后返回
parameterHandler =
(ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);
方法内部:获取所有的Interceprot(插件实现接口)(拦截器)
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)i$.next();
}
return target;
}
// 返回target对象
target = interceptor.plugin(target))
插件机制:创建代理对象,通过增强拦截每一个方法执行,然后在方法前后执行增强方法
编写插件
- 编写Interceptor实现类
public class MyPluginA implements Interceptor {
/**
* 拦截目标对象的目标方法的执行;
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 执行目标方法
Object proceed = invocation.proceed();
// 返回执行后的返回值
return proceed;
}
/**
* 包装梅花表对象,为目标对象创建一个代理对象
* @param o
* @return
*/
@Override
public Object plugin(Object o) {
// 接住Plugin类的wrap方法来使用当前Interceptor包装目标对象
Object wrap = Plugin.wrap(o, this);
// 返回动态代理对象
return wrap;
}
/**
* 将插件注册时的property属性设置进来
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的信息:" + properties);
}
}
- 使用@Intercepts注解完成插件签名
// 完成插件签名,告诉mybatis当前插件用来拦截哪个对象的哪个方法;
@Intercepts({
@Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
})
- 将写好的插件注册到配置文件当中
<plugins>
<plugin interceptor="per.xgt.config.MyPluginA">
<property name="username" value="root"/>
<property name="password" value="root"/>
</plugin>
</plugins>
多个插件运行流程
创建动态代理的时候,是按照插件配置顺序创建层层代理对象,执行目标方法的之后,按照逆向顺序执行;
功能修改
// 完成插件签名,告诉mybatis当前插件用来拦截哪个对象的哪个方法;
@Intercepts({
@Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
})
public class MyPluginA implements Interceptor {
/**
* 拦截目标对象的目标方法的执行;
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept===:"+invocation.getMethod());
// 改变方法参数
System.out.println("当前拦截的目标对象:"+invocation.getTarget());
Object target = invocation.getTarget();
// 拿到target的源数据
MetaObject metaObject = SystemMetaObject.forObject(target);
Object value = metaObject.getValue("parameterHandler.parameterObject");
System.out.println("所有参数:"+value);
// 修改完要用的参数
metaObject.setValue("parameterHandler.parameterObject",10);
// 执行目标方法
Object proceed = invocation.proceed();
// 返回执行后的返回值
return proceed;
}
/**
* 包装梅花表对象,为目标对象创建一个代理对象
* @param o
* @return
*/
@Override
public Object plugin(Object o) {
System.out.println("plugin===:" + o);
// 接住Plugin类的wrap方法来使用当前Interceptor包装目标对象
Object wrap = Plugin.wrap(o, this);
// 返回动态代理对象
return wrap;
}
/**
* 将插件注册时的property属性设置进来
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置的信息===:" + properties);
}
}
分页插件PageHelper
引入依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
配置
- 没有Spring直接配置在mybatis配置文件中
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>
- 和Spring整合配置
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!--指定mybatis的全局配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--指定mapper文件的位置-->
<property name="mapperLocations" value="classpath*:per/xgt/dao/*.xml"></property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">mysql</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
- PageHelper参数
分页插件可选参数如下:
- dialect:默认情况下会使用 PageHelper 方式进行分页,如果想要实现自己的分页逻辑,可以实现 Dialect(com.github.pagehelper.Dialect) 接口,然后配置该属性为实现类的全限定名称。
下面几个参数都是针对默认 dialect 情况下的参数。使用自定义 dialect 实现时,下面的参数没有任何作用。
- helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。
配置时,可以使用下面的缩写值:oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页。
你也可以实现 AbstractHelperDialect,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。 - offsetAsPageNum:默认值为 false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
- rowBoundsWithCount:默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。
- pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
- reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
- params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值;
可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值;
默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。 - supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。
- autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver),用法和注意事项参考下面的场景五。
- closeConn:默认值为 true。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。
重要提示:当 offsetAsPageNum=false 的时候,由于 PageNum 问题,RowBounds查询的时候 reasonable 会强制为 false。使用 PageHelper.startPage 方法不受影响。
简单使用
@RequestMapping("findAllEmps")
public String emps(Map<String,Object> map){
PageHelper.startPage(1, 5);
List<Employee> emps = employeeService.findAllEmps();
System.out.println("======");
for (Employee emp : emps) {
System.out.println(emp);
}
System.out.println("======");
PageInfo<Employee> list = new PageInfo<Employee>(emps);
map.put("pageInfo", list);
System.out.println(list);
List<Employee> employees = list.getList();
for (Employee employee : employees) {
System.out.println(employee);
}
return "list";
}
注意:
- 一定要在查询方法的前一步使用PageHelper.startPage(首页,页面大小);
- 想要使用页面的信息时使用pageInfo;
- PageInfo<实体类> pageInfo = new PageInfo<>(数据库中获取到的信息);
pageInfo内的内容
public class PageInfo<T> implements Serializable {
private static final long serialVersionUID = 1L;
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow 和endRow 不常用,这里说个具体的用法
//可以在页面中"显示startRow 到endRow 共size 条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
}
Page内的内容
public class Page<E> extends ArrayList<E> {
private static final long serialVersionUID = 1L;
//当前页码
private int pageNum;
//每一页记录数
private int pageSize;
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页码
private int pages;
//
private boolean count;
//
private Boolean reasonable;
//
private Boolean pageSizeZero;
}
批量BatchExecutor
@Test
public void test18() throws IOException {
SqlSessionFactory sqlSessionFactory = get();
// 可以执行批量操作的SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
long start = System.currentTimeMillis();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
for (int i = 0; i < 10000; i++) {
employee.setEmpGender(i%2==0?"0":"1");
employee.setEmpName("批量"+(i+1));
employee.setEmpEmail(employee.getEmpName() + "@163.com");
int batch = mapper.addEmpsBatch(employee);
}
sqlSession.commit();
long end = System.currentTimeMillis();
sqlSession.close();
System.out.println("执行时长:" + (end - start));
}
Spring中使用批量SqlSession
- spring配置文件
<!--配置一个可以批量执行的SqlSession-->
<bean class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>
- 使用
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Autowired
private SqlSession sqlSession;
@Override
public List<Employee> findAllEmps() {
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
// 执行批量操作
/*
* xxxxxx
* */
List<Employee> emps = employeeMapper.findAllEmps();
return emps;
}
}
Oracle的存储过程
使用游标返回数据列表
- 结果集对象
public class Page {
private int start;
private int end;
private int count;
private List<Employee> emps;
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public List<Employee> getEmps() {
return emps;
}
public void setEmps(List<Employee> emps) {
this.emps = emps;
}
}
- 调用存储过程
<!--存储过程的调用
使用select标签调用存储过程
statementType:CALLABLE标识调用存储过程
语法:
call 存储过程名(参数)
-->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
{
call emp_getpage(
#{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 id="pageEmp" type="per.xgt.entity.Employee">
<id column="id" property="id" />
<result column="emp_name" property="empName" />
<result column="emp_email" property="empEmail" />
<result column="emp_gender" property="empGender" />
</resultMap>
枚举类型
mybatis 处理枚举类型默认是存储枚举的名字,可以改为存储枚举的索引
<typeHandlers>
<!--指定使用枚举的索引进行存储
javaType:在处理哪个索引时用handler处理器;
-->
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="per.xgt.entity.EmpStatus"/>
</typeHandlers>
public enum EmpStatus {
LOGIN,LOGOUT,REMOVE
}
public class Employee implements Serializable {
private Integer id;
private String empName;
private String empGender;
private String empEmail;
private Department dept;
// 员工状态
private EmpStatus empStatus = EmpStatus.LOGOUT;
public EmpStatus getEmpStatus() {
return empStatus;
}
public void setEmpStatus(EmpStatus empStatus) {
this.empStatus = empStatus;
}
public Employee(Integer id, String empName, String empGender, String empEmail, EmpStatus empStatus) {
this.id = id;
this.empName = empName;
this.empGender = empGender;
this.empEmail = empEmail;
this.empStatus = empStatus;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", empName='" + empName + '\'' +
", empGender='" + empGender + '\'' +
", empEmail='" + empEmail + '\'' +
", dept=" + dept +
", empStatus=" + empStatus +
'}';
}
public Employee() {
}
public Employee(Integer id, String empName, String empGender, String empEmail, Department dept) {
this.id = id;
this.empName = empName;
this.empGender = empGender;
this.empEmail = empEmail;
this.dept = dept;
}
public Employee(Integer id, String empName, String empGender, String empEmail) {
this.id = id;
this.empName = empName;
this.empGender = empGender;
this.empEmail = empEmail;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpGender() {
return empGender;
}
public void setEmpGender(String empGender) {
this.empGender = empGender;
}
public String getEmpEmail() {
return empEmail;
}
public void setEmpEmail(String empEmail) {
this.empEmail = empEmail;
}
}
public int insertEmpByEnum(Employee employee);
<insert id="insertEmpByEnum" parameterType="per.xgt.entity.Employee" useGeneratedKeys="true" keyProperty="id">
insert into emp (emp_name,emp_gender,emp_email,emp_status)
values (#{empName},#{empGender},#{empEmail},#{empStatus})
</insert>
测试
@Test
public void test19() throws IOException {
SqlSessionFactory sqlSessionFactory = get();
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee(null, "enum", "1", "enum@163.com");
mapper.insertEmpByEnum(employee);
System.out.println(employee.getId());
sqlSession.close();
}
自定义枚举类型处理器
- 枚举类
public enum EmpStatus {
LOGIN(1001,"已登录"),
LOGOUT(1002,"已登出"),
REMOVE(1003,"不存在");
private Integer code;
private String msg;
private EmpStatus(int 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 1001:return LOGIN;
case 1002:return LOGOUT;
case 1003:return REMOVE;
default:return LOGOUT;
}
}
}
- mybatis配置
<typeHandlers>
<typeHandler handler="per.xgt.config.MyEnumHandler" javaType="per.xgt.entity.EmpStatus"/>
</typeHandlers>
- 标签内指定使用类型处理器
#{empStatus,typeHandler=per.xgt.config.MyEnumHandler}
如果是查询可以在resultMap中指定
<result column="empName" property="emp_name" typeHandler="per.xgt.config.MyEnumHandler"/>