一、介绍
MyBatis介绍
MyBatis是一流的持久性框架,支持自定义SQL,存储过程和高级映射。MyBatis消除了几乎所有的JDBC代码以及参数的手动设置和结果检索。MyBatis可以使用简单的XML或注释进行配置,并将图元,映射接口和Java POJO(普通的旧Java对象)映射到数据库记录。
官网介绍:https://mybatis.org/mybatis-3/
为什么要使用MyBatis
- MyBatis是一个半自动化的持久化层框架。
- JDBC
- SQL夹在Java代码块里,耦合度高导致硬编码内伤
- 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见 • Hibernate和JPA
- 长难复杂SQL,对于Hibernate而言处理也不容易
- 内部自动生产的SQL,不容易做特殊优化。
- 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。
导致数据库性能下降。 对开发人员而言,核心sql还是需要自己优化
- MyBatis
- sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
下载MyBatis
https://github.com/mybatis/mybatis-3/
打开页面后,即可找到下载地址
如果是maven项目直接引入mybatis依赖即可
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
二、操作
2.1、原理介绍
根据全局配置文件,利用SqlSessionFactoryBuilder创建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder().build(inputStream);
使用SqlSessionFactory获取sqlSession对象。一个SqlSession对象代表和数据库的一次会话
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
使用SqlSession根据方法id进行操作
try {
Employee employee = openSession.selectOne(
"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
} finally {
openSession.close();
}
SqlSession
- SqlSession 的实例不是线程安全的,因此是不能被共享的。
- SqlSession每次使用完成后需要正确关闭,这个关闭操作是必须的。
- SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作。
2.2、代码实战
创建MyBatis全局配置文件mybatis-config.xml
MyBatis 的全局配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息、如数据库连接池信息等。指导着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.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
properties属性:
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
创建SQL映射文件EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
<!--
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
-->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
创建DAO层Mapper接口EmployeeMapper
package com.atguigu.mybatis.dao;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
测试
package com.atguigu.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.EmployeeMapper;
/**
* @author sgw
*/
public class MyBatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 1、根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 有数据源一些运行环境信息
* 2、sql映射文件;配置了每一个sql,以及sql的封装规则等。
* 3、将sql映射文件注册在全局配置文件中
* 4、写代码:
* 1)、根据全局配置文件得到SqlSessionFactory;
* 2)、使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
* 一个sqlSession就是代表和数据库的一次会话,用完关闭
* 3)、使用sql的唯一标志来告诉MyBatis执行哪个sql。sql都是保存在sql映射文件中的。
*
* @throws IOException
*/
@Test
public void test() throws IOException {
// 2、获取sqlSession实例,能直接执行已经映射的sql语句
// sql的唯一标识:statement Unique identifier matching the statement to use.
// 执行sql要用的参数:parameter A parameter object to pass to the statement.
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
Employee employee = openSession.selectOne(
"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
} finally {
openSession.close();
}
}
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//MyBatis会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
}
总结
-
1、接口式编程
原生: Dao ====> DaoImpl
mybatis: Mapper ====> xxMapper.xml -
2、SqlSession
SqlSession 代表和数据库的一次会话;用完必须关闭;
SqlSession和Connection一样都是非线程安全的。每次使用都应该去获取新的对象(不要放在成员变量里)。 -
3、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
(将接口和xml进行绑定)
EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class); -
5、两个重要的配置文件:
mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等…系统运行环境信息
sql映射文件:保存了每一个sql语句的映射信息,将sql抽取出来。
2.3、全局配置文件mybatis-config.xml
properties属性
官方介绍
- mybatis可以使用properties来引入外部properties配置文件的内容;
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="dbconfig.properties"></properties>
<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>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<!-- 6、mappers:将sql映射注册到全局配置中 -->
<mappers>
<!--
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
注册接口
class:引用(注册)接口,
1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2、没有sql映射文件,所有的sql都是利用注解写在接口上;
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件
不重要,简单的Dao接口为了开发快速可以使用注解;
-->
<!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
<!-- 批量注册: -->
<package name="com.atguigu.mybatis.dao"/>
</mappers>
</configuration>
对应的dbconfig.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
settings属性
- settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
例如:设置开启自动驼峰命名策略(mapUnderscoreToCamelCase默认值是false,不开启),即数据库字段是下划线A_COLUMN,而javaBean里是aColumn,开启驼峰命名后,这样就会把数据库里的字段映射到javaBean里了;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="dbconfig.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<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>
</environments>
<mappers>
<!-- 批量注册: -->
<package name="com.atguigu.mybatis.dao"/>
</mappers>
</configuration>
typeAliases别名属性
官方文档
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
typeAliases:别名处理器:可以为我们的java类型起别名 ,别名不区分大小写;
不指定别名,使用默认的别名:
<typeAliases>
<!-- 1、typeAlias:为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写;employee
-->
<typeAlias type="com.atguigu.mybatis.bean.Employee" />
</typeAliases>
指定别名:
<typeAliases>
<!-- alias:指定新的别名-->
<typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/>
</typeAliases>
为某个包下的所有类批量起别名:
<typeAliases>
<!-- 2、package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(别名是类名小写))
-->
<package name="com.atguigu.mybatis.bean"/>
</typeAliases>
使用上边package批量起别名的前提下,在javaBean上使用@Alias注解为每个实体类起自己的起别名:
//别名
Alias("emp")
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", gender=" + gender + "]";
}
}
在mybatis-config.xml配置了别名后,在Mapper.xml的文件中,所有写com.atguigu.mybatis.bean.Employee
的地方(一般是resultType的值)都可以换成别名了
MyBatis已经起好的别名:
注意:我们自己的别名不可以与上边的重复
typeHandlers类型处理器
官方文档
typeHandlers是架起javaBean与数据库的桥梁,即通过typeHandlers将java里的数据类型与数据库里的数据类型进行一一映射;
typeHandlers后边会细讲;
plugins插件
官方文档
主要是在拦截以下四大对象前后做一些事情
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
plugins插件后边会详细介绍;
environments环境配置
官方文档
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中;
- environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
- environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
- transactionManager:事务管理器(项目中的事务一般交给了Spring去管理,不在MyBatis配置);
- type:事务管理器的类型(有以下两种事务管理器):
JDBC(JdbcTransactionFactory) | MANAGED(ManagedTransactionFactory)
自定义事务管理器:实现TransactionFactory接口.type指定为全类名
- type:事务管理器的类型(有以下两种事务管理器):
- dataSource:数据源
- type:数据源类型
UNPOOLED(UnpooledDataSourceFactory)不使用连接池
POOLED(PooledDataSourceFactory)使用连接池
JNDI(JndiDataSourceFactory)使用连接池
自定义数据源:实现DataSourceFactory接口,type是全类名
- type:数据源类型
<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>
databaseIdProvider数据库厂商标识
官方文档
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId 属性;
在全局平配置文件里进行配置来支持多数据库厂商
- type=“DB_VENDOR”:VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
MySQL,Oracle,SQL Server,xxxx
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 value值是别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
全局配置好后,在Mapper.xml里,通过databaseId的值告诉MyBatis当前sql是那种数据库的语句
<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>
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
mappers映射器
官方文档
mappers:将sql映射注册到全局配置中
mapper:注册一个sql映射
方式一:注册配置文件
- resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
- url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
<mappers>
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
</mappers>
方式二:注册接口
class:引用(注册)接口,
- 有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
- 没有sql映射文件,所有的sql都是利用注解写在接口上;
sql利用注解写在接口上的方式:
package com.atguigu.mybatis.dao;
import org.apache.ibatis.annotations.Select;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id=#{id}")
public Employee getEmpById(Integer id);
}
全局配置文件:
<mappers>
<mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/>
</mappers>
以上两种方式可以同时配置
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
<mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/>
方式三:批量注册
映射文件名必须和接口同名,并且放在与接口同一目录下(注解版不需要);
<mappers>
<package name="com.atguigu.mybatis.dao"/>
</mappers>
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件;
不重要,简单的Dao接口为了开发快速可以使用注解的方式;
一个比较完整的全局配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="dbconfig.properties"></properties>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.atguigu.mybatis.bean"/>
</typeAliases>
<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>
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
<mappers>
<package name="com.atguigu.mybatis.dao"/>
</mappers>
</configuration>
注意,配置文件里的标签是有顺序的,比如 <databaseIdProvider >
标签不可以放在<environments>
之前
标签的顺序如下:
三、MyBatis的sql映射文件Mapper.xml
3.1、MyBatis实现增删改操作
接口
package com.atguigu.mybatis.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
public Long addEmp(Employee employee);
public boolean updateEmp(Employee employee);
public void deleteEmpById(Integer 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="com.atguigu.mybatis.dao.EmployeeMapper">
<!-- parameterType:参数类型,可以省略,
获取自增主键的值:
mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();
useGeneratedKeys="true";使用自增主键获取主键值策略
keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
-->
<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>
<!--
获取非自增主键的值:
Oracle不支持自增;Oracle使用序列来模拟自增;
每次插入的数据的主键是从序列中拿到的值;如何获取到这个值;
-->
<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>
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},email=#{email},gender=#{gender}
where id=#{id}
</update>
<delete id="deleteEmpById">
delete from tbl_employee where id=#{id}
</delete>
</mapper>
测试:
/**
* 测试增删改
* @throws IOException
*/
@Test
public void test03() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
Employee employee = new Employee(null, "jerry4",null, "1");
mapper.addEmp(employee);
System.out.println(employee.getId());
//测试修改
//Employee employee = new Employee(1, "Tom", "jerry@atguigu.com", "0");
//boolean updateEmp = mapper.updateEmp(employee);
//System.out.println(updateEmp);
//测试删除
//mapper.deleteEmpById(2);
//2、手动提交数据
openSession.commit();
}finally{
openSession.close();
}
}
- mybatis允许增删改直接定义以下类型返回值
Integer、Long、Boolean、void - 有以下两种SqlSession
SqlSession openSession =sqlSessionFactory.openSession();——>需要手动提交
SqlSession openSession =sqlSessionFactory.openSession(true);——>自动提交
上边的插入操作,主键值是自增的,当执行完插入操作后,想得到返回的主键时,MyBatis利用statement.getGenrentedKeys()来获取,只需要在<insert>
节点里配置useGeneratedKeys="true"
即可
,默认是false;keyProperty="id"
指定主键对应的属性,即MyBatis获取到主键值后保存到JavaBean的id
属性里;
<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>
oracle不支持自增,需要使用序列来实现自增,每次插入的数据的主键是从序列中拿到的值,那如何获取到这个值?
<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>
3.2、MyBatis对参数的处理
单个参数
mybatis不会做特殊处理,#{参数名/任意名}:取出参数值。注意,大括号里写任意值都行,没必要与接口方法的参数名保持一致
接口:
import java.util.List;
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
映射文件
<!--注意大括号里,是可以随便写的,因为只有一个参数-->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id = #{idabc}
</select>
多个参数
接口
public Employee getEmpByIdAndLastName(Integer id,String lastName);
映射文件
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
上边这样是会报错的:
org.apache.ibatis.binding.BindingException:
Parameter ‘id’ not found.
Available parameters are [1, 0, param1, param2]
这是因为MyBatis遇到多个参数的时候会做特殊处理,多个参数会被封装成 一个map,key的值是param1…paramN,或者参数的索引也可以,value的值是传入的参数值,所以,接口如果不变的情况下,映射文件正确的写法应该是:
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id = #{param1} and last_name=#{param2}
</select>
上边这样写没有问题,但是阅读起来很不友好,所以采用@Param命名参数的方式,明确指定封装参数时map的key;@Param(“id”),多个参数会被封装成 一个map,key:使用@Param注解指定的值,value:参数值,#{指定的key}取出对应的参数值;
接口:
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
映射文件:
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>
POJO作为参数
如果多个参数正好是我们业务逻辑的数据模型(javaBean),我们就可以直接传入pojo;
#{属性名}:即可取出传入的pojo的属性值
Map作为参数
如果多个参数不是业务模型中的数据(javaBean),没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的值;
接口
public Employee getEmpByMap(Map<String, Object> map);
映射文件
<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
select * from ${tableName} where id=${id} and last_name=#{lastName}
</select>
几种特殊参数形式,对于的mapper取值的写法
1、接口
public Employee getEmp(@Param("id")Integer id,String lastName);
对应的mapper取值:
id==>#{id/param1} lastName==>#{param2}
2、接口
public Employee getEmp(Integer id,@Param("e")Employee emp);
对应的mapper取值:
id==>#{param1} lastName===>#{param2.lastName/e.lastName}
3、特别注意:如果参数是Collection(List、Set)类型、或者是数组类型,mybatis也会特殊处理,会把传入的list或者数组封装在map中
map里key的值:
- 参数是Collection:key就是collection,如果Collection是List,key可以使用这个list
- 参数是数组Array:key就是array
例如接口:
public Employee getEmpById(List<Integer> ids);
对应mapper里的取值:
取出第一个id的值: #{list[0]}
mybatis中#与$的区别
$会自动将参数填充到指定位置,而#会使用占位符填充
区别:
- #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement预编译,可以防止sql注入;
- ${}:取出的值直接拼装在sql语句中,存在安全问题;
大多情况下,我们取参数的值都应该去使用#{};
原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如分表、排序。。。;按照年份分表拆分
# "#"只能获取参数里的值,sql里表名的地方不是参数,不可以使用#来获取表名
select * from ${year}_salary where xxx;
# 表名、排序,不支持预编译,即不可以使用#来取值
select * from tbl_employee order by ${f_name} ${order}
#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错):
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;
由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;如果email字段为空,有两种解决办法
1、mapper文件里取值的地方:#{email,jdbcType=OTHER};
2、修改全局配置:jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>
3.3、select查询元素
返回类型是List集合
如果返回的是一个list集合类型,则resultType要写集合中元素的类型
接口:
public List<Employee> getEmpsByLastNameLike(String lastName);
mapper文件
<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
返回类型是Map集合
1、返回结果只有一条Map记录
如果返回的是一个Map集合类型,且只有一条记录,则resultType就是map(mybatis为Map集合取的别名)
接口:
//返回一条记录的map;key就是列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
mapper文件
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>
2、返回结果只有多条Map记录
如果返回的是一个Map集合类型,且有多条记录,则resultType是集合里元素的类型
接口:
//多条记录封装一个map:Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("lastName")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
mapper.xml:
<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
resultType自定义结果集映射
数据库列名与javaBean属性名对应不上的话,可以有三种解决方式
1、起别名
2、全局配置文件里开启驼峰命名(前提是数据库列符合驼峰规则)
3、使用resultMap来自定义映射
切记:resultType与resultType只能使用其中的一个
mapper.xml里自定义映射规则
<!--自定义某个javaBean的封装规则
type:自定义规则的Java类型
id:唯一id,方便引用
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
<!--指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装:我们只要写了resultMap,就把全部的映射规则都写上。 -->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<select id="getEmpById" resultMap="MySimpleEmp">
select * from tbl_employee where id=#{id}
</select>
场景:
查询Employee员工的同时,查询该员工对应的部门
Employee===Department;
一个员工有与之对应的部门信息;
接口:
public Employee getEmpAndDept(Integer id);
方式一、级联属性封装结果集
resultMap 结果集:
<!--
联合查询:级联属性封装结果集
-->
<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>
查询sql:
<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(联合)定义关联的单个对象的封装规则
resultMap 结果集:
<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>
方式三、使用association进行分步查询
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值去部门表查出部门信息
3、将部门信息设置到员工属性中;
定义部门的接口(之前只有员工的接口):
public Department getDeptById(Integer id);
对应的部门mapper.xml:
<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
员工接口:
public Employee getEmpByIdStep(Integer id);
员工mapper.xml:
<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指定的属性(dept)
-->
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
<if test="_parameter!=null">
and 1=1
</if>
</select>
association 的延迟加载(懒加载):
上边的场景里,我们每次查询Employee对象的时候,都将Department一起查询出来了。
延迟加载:部门信息在我们使用的时候再去查询;
要实现懒加载,在之前association 分段查询的基础之上,在全局配置文件里加上以下两个配置即可:
<settings>
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
javaBean里的属性是集合类型
方式一:嵌套集合
场景:查询部门的时候,将当前部门里所有员工都查出来(部门的javaBean里,员工属性是集合类型)
部门接口:
public Department getDeptByIdPlus(Integer id);
部门mapper.xml:
<!--嵌套结果集的方式,使用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>
<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>
方式二:分步查询
接口:
public Department getDeptByIdStep(Integer id);
mapper.xml:
<!-- 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="{id}" fetchType="lazy">
</collection>
</resultMap>
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name from tbl_dept where id=#{id}
</select>
扩展:多列的值传递过去,将多列的值封装map传递,如下:
column="{key1=column1,key2=column2}"
即:
<collection property="emps"
select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="{deptId=id}" fetchType="lazy">
</collection>
fetchType=“lazy”:表示使用延迟加载,即懒加载;
- lazy:延迟
- eager:立即
discriminator鉴别器
mybatis可以使用discriminator判断某列的值,然后根据该列的值改变封装行为
场景:如果查出的员工是女生:就把部门信息查询出来,否则不查询部门信息;如果是男生,把last_name这一列的值赋值给email;
mapper.xml:
<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>
四、动态sql
4.1 MyBatis之if标签
场景:当查询条件有多个字段时,则sql里就将那些不为空的字段作为查询条件
接口:
public List<Employee> getEmpsByConditionIf(Employee employee);
mapper.xml:
<select id="getEmpsByConditionIf" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
<!-- where -->
<where>
<!-- test:判断表达式(OGNL)
OGNL参照官方文档。
c:if test
从参数中取值进行判断
遇见特殊符号应该去写转义字符:
"":指的是'',即空字符;
&&:指的是&&,即条件与;
-->
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null && lastName!=""">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=""">
and email=#{email}
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</where>
</select>
4.2 MyBatis之where标签
4.1章节中,已经使用了where条件,mybatis会将where标签中拼装的sql,多出来的and或者or去掉,where只会去掉第一个多出来的and或者or,即and或or只能放在if标签里的最前边。
4.3 MyBatis之trim标签
trim标签可以自定义字符串的截取规则
接口:
public List<Employee> getEmpsByConditionTrim(Employee employee);
mapper.xml:
<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 && lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
</select>
4.4 MyBatis之choose(when, otherwise)标签
choose标签,即分支选择,相当于java里带了break的swtich-case;
场景:
如果id不为空,则使用id作为查询条件,如果lastName不为空,则使用lastName作为查询条件,即最后只使用其中一种作为条件;
接口:
public List<Employee> getEmpsByConditionChoose(Employee employee);
mapper.xml:
<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>
4.5 MyBatis之set标签
where是用来封装查询条件的, 而set是用来封装修改条件的;
接口:
public void updateEmp(Employee employee);
mapper.xml:
<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>
4.6 MyBatis之foreach标签
foreach标签用来遍历集合
4.6.1、foreach遍历查询
接口:
public List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);
mapper.xml:
<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>
4.6.2、foreach遍历插入
接口:
public void addEmps(@Param("emps")List<Employee> emps);
场景一:MySql数据库
mapper.xml;
<!--MySQL下批量保存:可以foreach遍历 mysql支持values(),(),()语法-->
<insert id="addEmps">
insert into tbl_employee(
last_name,email,gender,d_id
)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
mapper.xml也可以这样写(执行多条插入语句):
<!--这种方式需要数据库连接属性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>
在properties里配置allowMultiQueries=true:
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
场景二:Oracle数据库
方式一:begin-end方式
即在oracle直接执行的语句就是:
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;
mapper.xml:
<insert id="addEmps" databaseId="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>
方式二:利用中间表
即在oracle直接执行的语句就是:
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
)
mapper.xml:
<insert id="addEmps" databaseId="oracle">
<!-- oracle第二种批量方式 -->
insert into employees(
employee_id,last_name,email
)
select employees_seq.nextval,lastName,email from
(
<foreach collection="emps" item="emp" separator="union">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
)
</insert>
上边mapper.xml优化:
<insert id="addEmps" databaseId="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>
4.7 两个内置参数
不只是方法传递过来的参数可以被用来判断、取值,mybatis默认还有两个内置参数
参数一 :_parameter
_parameter代表整个参数
接口传递单个参数:_parameter就是这个参数
接口传递多个参数:参数会被封装为一个map,_parameter就是代表这个map
参数二 :_databaseId
如果全局配置文件配置了databaseIdProvider标签,则_databaseId就是代表当前数据库的别名,如oracle、mysql等
全局配置文件:
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
接口:
public List<Employee> getEmpsTestInnerParameter(Employee employee);
mapper.xml:
<select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
<if test="_databaseId=='mysql'">
select * from tbl_employee
<if test="_parameter!=null">
where last_name like #{lastName}
</if>
</if>
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter!=null">
where last_name like #{_parameter.lastName}
</if>
</if>
</select>
bind属性
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
<select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
<!-- bind:将lastName的值取到后,拼接上模糊查询%,赋值给_lastName-->
<bind name="_lastName" value="'%'+lastName+'%'"/>
<if test="_databaseId=='mysql'">
select * from tbl_employee
<if test="_parameter!=null">
where last_name like #{_lastName}
</if>
</if>
</select>
4.8 MyBatis之sql标签与include标签
sql标签抽取可重用的sql片段,方便后面引用(使用include引用)
<sql id="insertColumn">
employee_id,last_name,email
</sql>
引用上边的标签
<insert id="addEmps" databaseId="oracle">
insert into employees(
<!-- 引用外部定义的sql -->
<include refid="insertColumn"></include>
)
<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>
动态判断
<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>
include还可以自定义一些property,sql标签内部就能使用自定义的属性
include-property:取值的正确方式${prop},#{不能使用这种方式}
<insert id="addEmps" databaseId="oracle">
insert into employees(
<!-- 引用外部定义的sql -->
<include refid="insertColumn">
<property name="testColomn" value="abc"/>
</include>
)
<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>
4.9 resultMap与resultType区别
mybatis中在查询进行select映射(表字段与实体类映射)的时候,返回类型可以用resultType,也可以用resultMap。
resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。
实体类:
public class User {
private int id;
private String username;
private String hashedPassword;
//省略setter/getter方法
}
使用resultType
<select id="selectUsers" parameterType="int" resultType="com.maltose.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
这种情况下,MyBatis 会在幕后自动创建一个 ResultMap,基于属性名来映射列到 JavaBean 的属性上。如果数据库列名与实体类属性名没有精确匹配,可以在列名上使用 select 语句的别名来匹配标签。
使用resultMap
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
<select id="selectUsers" parameterType="int" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
不同点:
- resultType对应的是java对象中的属性,大小写不敏感;resultMap对应的是对已经定义好了id的resultType的引用,key是查询语句的列名,value是查询的值,大小写敏感;
- 使用resultType的时候,要保证结果集的列名与java对象的属性相同,而resultMap则不用,可以配置属性与列名一一对应即可。
- 另外,resultMap 元素,它是 MyBatis 中最重要最强大的元素,它能提供级联查询,缓存等功能;
五、MyBatis缓存机制
MyBatis包含非常强大的缓存机制,可以进行方便的配置和定义,极大的提高查询效率;MyBatis定义了两级缓存,默认开启一级缓存;
5.1 一级缓存
一级缓存也叫本地缓存,是sqlSession级别的缓存。一级缓存是一直开启的,无法关闭;
SqlSession级别的一个Map:即MyBatis查出数据后,会将数据放在当前SqlSession的Map里;
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
- sqlSession不同。
- sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)。
- sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)。
- sqlSession相同,手动清除了一级缓存(缓存清空:openSession.clearCache())。
5.2 二级缓存
二级缓存是全局缓存,基于namespace级别的缓存,即一个namespace对应一个二级缓存;
工作机制:
1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以查找二级缓存中的内容;
3、假如sqlSession里有两大块内容:EmployeeMapper(Employee)与DepartmentMapper(Department),则二级缓存会根据不同namespace的值将这两部门内容放在不同的Map里存储起来;不同namespace查出的数据会放在自己对应的缓存中(map);
效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中。
注意:
只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
开启二级缓存的步骤:
- 开启全局二级缓存配置(默认已开启):
如果设置为false的话,关闭的是二级缓存,不是一级缓存,一级缓存无法关闭;
<setting name="cacheEnabled" value="true"/>
- 去要开启二级缓存的mapper.xml中配置使用二级缓存:
<cache></cache>
也可以配置缓存的回收策略,即缓存太多后,删除哪些缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<!-- <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> -->
<!--
eviction:缓存的回收策略:
• LRU(默认策略) – 最近最少使用的:移除最长时间不被使用的对象。
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• 默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名;
缓存类实现Cache接口即可;
-->
- 我们的POJO需要实现序列化接口
关于缓存的设置/属性讲解
- cacheEnabled=false:false关闭的是二级缓存,没有关闭一级缓存,一级缓存一直可用
- 每个select标签都有一个属性useCache=“true”:
设置为false:不使用二级缓存,一级缓存依然使用 - 每个增删改标签的:flushCache=“true”:(一级二级都会清除)
增删改执行完成后就会清楚缓存;
测试:flushCache=“true”:一级缓存与二级都会被清除;
查询标签:flushCache=“false”:
如果flushCache=true;每次查询之后都会清空缓存,缓存是没有被使用的; - sqlSession.clearCache()方法:只是清除当前session的一级缓存;
- localCacheScope:本地缓存作用域,有以下两种取值:SESSION与STATEMENT;默认是SESSION,SESSION是一级缓存,将当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;
MyBatis查询数据的顺序:
先找二级缓存(因为二级缓存范围广),再找一级缓存,都没有的话再去数据库查数据;
六、MyBatis与Spring(以及SpringMvc)整合
6.1 介绍
mybatis官网:https://github.com/mybatis
MyBatis整合Spring需要的jar包
整合文档
各个版本对应表格
整合包下载:
6.2 SSM整合
导入需要的jar包
Spring与SpringMvc需要的jar包
MyBatis需要的jar包:
Spring与MyBatis整合的jar包
数据库驱动需要的jar包
编写配置文件
MyBatis全局配置文件mybatis-config.xml
<?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="mapUnderscoreToCamelCase" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
<!--显式的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
</configuration>
数据库配置文件dbconfig.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
jdbc.username=root
jdbc.password=123456
orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456
MyBatis接口:
package com.maltose.mybatis.dao;
import java.util.List;
import com.atguigu.mybatis.bean.Employee;
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
public List<Employee> getEmps();
}
MyBatis的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="com.atguigu.mybatis.dao.EmployeeMapper">
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where id=#{id}
</select>
<select id="getEmps" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
</select>
</mapper>
配置web.xml,使得SpringMvc的IOC容器跟随项目一起启动
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
配置web.xml,添加SpringMvc的配置文件
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
spring配置文件applicationContext.xml:
<?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:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- Spring希望管理所有的业务逻辑组件,等。。。 -->
<context:component-scan base-package="com.maltose.mybatis">
<!--除了Controller控制器不扫描以外,其他都扫描-->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- 引入数据库的配置文件 -->
<context:property-placeholder location="classpath:dbconfig.properties" />
<!-- Spring用来控制业务逻辑。数据源、事务控制、aop -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></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>
<!-- 开启基于注解的事务 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!--
整合mybatis
目的:1、spring管理所有组件,包含mapper的实现类。
service==>Dao @Autowired:自动注入mapper,不需要再自己创建openSession了;
2、spring用来管理事务,spring声明式事务
-->
<!--创建出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>
</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> -->
</beans>
在web.xml同级目录创建SpringMvc的配置文件spring-servlet.xml:
<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
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-4.0.xsd">
<!--SpringMVC只是控制网站跳转逻辑,即只管Controller -->
<!-- 只扫描控制器 -->
<context:component-scan base-package="com.maltose.mybatis" use-default-filters="false">
<!--只扫描当前包下标了@Controller注解的类-->
<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:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler/>
</beans>
6.3 MyBatis逆向工程—MBG
MyBatis逆向工程,即代码生成器,根据数据库里的表,创建出对应的javaBean、接口、mapper.xml文件
下载代码生成器
官网地址:https://github.com/mybatis
官方使用文档以及下载代码生成器:
点击下载即可:
使用MyBatis代码生成器
解压后将下边这个包导入到项目里
官方使用介绍:http://mybatis.org/generator/quickstart.html
在项目根目录下创建配置文件mbg.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime="MyBatis3Simple":生成简单版的CRUD
MyBatis3:豪华版
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- jdbcConnection:指定如何连接到目标数据库 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
userId="root"
password="123456">
</jdbcConnection>
<!-- java类型解析器 -->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- javaModelGenerator:指定javaBean的生成策略
targetPackage="test.model":生成的目标javaBean包名
targetProject="\MBGTestProject\src":目标工程
-->
<javaModelGenerator targetPackage="com.maltose.mybatis.bean"
targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- sqlMapGenerator:sql映射生成策略: -->
<sqlMapGenerator targetPackage="com.atguigu.mybatis.dao"
targetProject=".\conf">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- javaClientGenerator:指定mapper接口所在的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao"
targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定要逆向分析哪些表:根据表要创建javaBean,domainObjectName只对应的javaBean -->
<table tableName="tbl_dept" domainObjectName="Department"></table>
<table tableName="tbl_employee" domainObjectName="Employee"></table>
</context>
</generatorConfiguration>
新建一个测试类,加入如下方法:
package com.maltose.mybatis.test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.bean.EmployeeExample;
import com.atguigu.mybatis.bean.EmployeeExample.Criteria;
import com.atguigu.mybatis.dao.EmployeeMapper;
public class MyBatisTest {
@Test
public void testMbg() 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);
}
}
执行完上边的方法后,就会生成相应的文件;
代码生成器生成复杂逻辑的文件
以上是生成简单的增删改查文件,其实代码生成器也支持生成复杂的逻辑,如带条件的查询等等,将配置文件里的targetRuntime="MyBatis3Simple"改为targetRuntime="MyBatis3"即可;
6.4 MyBatis逆向工程—配置文件结合插件的方式
在resource目录下新建generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry location="E:\study\repository\mysql\mysql-connector-java\8.0.19\mysql-connector-java-8.0.19.jar"/>
<context id="test" targetRuntime="MyBatis3">
<!-- 一些工具 -->
<!-- <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"></plugin>-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin>
<!--<plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin>-->
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库链接 参数 mysql8之前:com.mysql.jdbc.Driver;
mysql8之后:com.mysql.cj.jdbc.Driver; -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://192.168.237.131:3306/mysql" userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成模型的包名和位置 -->
<javaModelGenerator targetPackage="com.qf.pojo"
targetProject="src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="true" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 生成映射文件的包名和位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 生成DAO的包名和位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.qf.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 要生成哪些表 -->
<table tableName="sys_menu" domainObjectName="SysMenu"></table>
<table tableName="sys_role" domainObjectName="SysRole"></table>
<table tableName="sys_user" domainObjectName="SysUser"></table>
<table tableName="sys_role_menu" domainObjectName="SysRoleMenu"></table>
<table tableName="sys_user_role" domainObjectName="SysUserRole"></table>
<table tableName="sys_config" domainObjectName="SysConfig"></table>
<table tableName="sys_log" domainObjectName="SysLog"></table>
<table tableName="schedule_job" domainObjectName="ScheduleJob"></table>
<table tableName="schedule_job_log" domainObjectName="ScheduleJobLog"></table>
<table tableName="activity" domainObjectName="Activity"></table>
<table tableName="activity_car" domainObjectName="ActivityCar"></table>
<table tableName="activity_category" domainObjectName="ActivityCategory"></table>
<table tableName="activity_channel" domainObjectName="ActivityChannel"></table>
<table tableName="activity_dealer" domainObjectName="ActivityDealer"></table>
<table tableName="activity_interface" domainObjectName="ActivityInterface"></table>
<table tableName="activity_key" domainObjectName="ActivityKey"></table>
<table tableName="activity_link" domainObjectName="ActivityLink"></table>
<table tableName="activity_pdata" domainObjectName="ActivityPdata"></table>
<table tableName="activity_tag" domainObjectName="ActivityTag"></table>
<table tableName="admin_menu_auth" domainObjectName="AdminMenuAuth"></table>
<table tableName="admin_role" domainObjectName="AdminRole"></table>
<table tableName="admin_role_menu" domainObjectName="AdminRoleMenu"></table>
<table tableName="admin_user" domainObjectName="AdminUser"></table>
<table tableName="admin_user_role" domainObjectName="AdminUserRole"></table>
<table tableName="area" domainObjectName="Area"></table>
<table tableName="article" domainObjectName="Article"></table>
<table tableName="article_action" domainObjectName="ArticleAction"></table>
<table tableName="article_category" domainObjectName="ArticleCategory"></table>
<table tableName="article_tag" domainObjectName="ArticleTag"></table>
<table tableName="car" domainObjectName="Car"></table>
<table tableName="car_brand" domainObjectName="CarBrand"></table>
<table tableName="car_image" domainObjectName="CarImage"></table>
<table tableName="car_make" domainObjectName="CarMake"></table>
<table tableName="car_model" domainObjectName="CarModel"></table>
<table tableName="car_model_image" domainObjectName="CarModelImage"></table>
<table tableName="car_select_list" domainObjectName="CarSelectList"></table>
<table tableName="choose_log" domainObjectName="ChooseLog"></table>
<table tableName="dict_answer" domainObjectName="DictAnswer"></table>
<table tableName="dict_brand_point" domainObjectName="DictBrandPoint"></table>
<table tableName="dict_country" domainObjectName="DictCountry"></table>
<table tableName="dict_model" domainObjectName="DictModel"></table>
<table tableName="dict_weight" domainObjectName="DictWeight"></table>
<table tableName="member" domainObjectName="Member"></table>
<table tableName="member_car" domainObjectName="MemberCar"></table>
<table tableName="member_fav" domainObjectName="MemberFav"></table>
<table tableName="member_login_log" domainObjectName="MemberLoginLog"></table>
<table tableName="member_point" domainObjectName="MemberPoint"></table>
<table tableName="member_profile" domainObjectName="MemberProfile"></table>
<table tableName="member_profile_log" domainObjectName="MemberProfileLog"></table>
<table tableName="member_tag" domainObjectName="MemberTag"></table>
<table tableName="member_token" domainObjectName="MemberToken"></table>
<table tableName="message" domainObjectName="Message"></table>
<table tableName="message_sendlog" domainObjectName="MessageSendlog"></table>
<table tableName="operate_log" domainObjectName="OperateLog"></table>
<table tableName="product" domainObjectName="Product"></table>
<table tableName="product_car" domainObjectName="ProductCar"></table>
<table tableName="product_car_activity" domainObjectName="ProductCarActivity"></table>
<table tableName="sdk_interface" domainObjectName="SdkInterface"></table>
<table tableName="sdk_interface_info" domainObjectName="SdkInterfaceInfo"></table>
<table tableName="sdk_interface_parm" domainObjectName="SdkInterfaceParm"></table>
<table tableName="sdk_interface_type" domainObjectName="SdkInterfaceType"></table>
<table tableName="sdk_interface_udparm" domainObjectName="SdkInterfaceUdparm"></table>
<table tableName="sdk_temp" domainObjectName="SdkTemp"></table>
<table tableName="tag" domainObjectName="Tag"></table>
<table tableName="tb_token" domainObjectName="TbToken"></table>
<table tableName="tb_user" domainObjectName="TbUser"></table>
<table tableName="visit_log" domainObjectName="VisitLog"></table>
<table tableName="visitor" domainObjectName="Visitor"></table>
<table tableName="car_param_type" domainObjectName="CarParamType"></table>
<table tableName="car_params" domainObjectName="CarParams"></table>
<table tableName="car_manufacturer" domainObjectName="CarManufacturer"></table>
</context>
</generatorConfiguration>
双击右侧执行mybatis插件即可,执行完就会生成左侧的接口、mapper、pojoi文件
七、MyBatis分页插件之PageHelper
官网:https://github.com/pagehelper/Mybatis-PageHelper
添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>x.x.x</version>
</dependency>
接口:
public List<Employee> getEmps();
mapper.xml:
<select id="getEmps" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name lastName,email,gender from tbl_employee
</select>
在MyBatis全局配置文件里配置拦截器
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
使用PageHelper实现分页:
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//获取第2页,每页显示三条数据
Page<Object> page = PageHelper.startPage(2, 3);
//执行下边的查询方法之前,执行上边的分页逻辑,得到的结果就是分页后的数据
List<Employee> emps = mapper.getEmps();
System.out.println("当前页码:"+page.getPageNum());
System.out.println("总记录数:"+page.getTotal());
System.out.println("每页的记录数:"+page.getPageSize());
System.out.println("总页码:"+page.getPages());
使用PageInfo获取更多分页数据:
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);
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());
八、MyBatis批量操作—BATCH
MyBatis未与Spring整合时实现批量操作
获取openSession 时,传入BATCH参数
@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();
}
}
MyBatis与spring整合之后,实现批量操作
applicationContext.xml里添加批量操作的配置
<!--配置一个可以进行批量执行的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>
配置完后,代码里使用:
package com.maltose.mybatis.service;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.maltose.mybatis.bean.Employee;
import com.maltose.mybatis.dao.EmployeeMapper;
@Service
public class EmployeeService {
@Autowired
private SqlSession sqlSession;
public List<Employee> getEmps(){
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
return mapper .getEmps();
}
}
九、MyBatis操作存储过程
场景:oracle实现分页
分页javaBean
package com.maltose.mybatis.bean;
import java.util.List;
/**
* 封装分页查询数据
* @author sgw
*
*/
public class OraclePage {
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;
}
}
在oracle里编写一个分页的存储过程
MyBatis调用存储过程来完成分页
接口:
public void getPageByProcedure(OraclePage page);
mapper.xml:
<!--
1、使用select标签定义调用存储过程
2、statementType="CALLABLE":表示要调用存储过程
3、{call procedure_name(params)}
-->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
{call hello_test(
#{start,mode=IN,jdbcType=INTEGER},
#{end,mode=IN,jdbcType=INTEGER},
#{count,mode=OUT,jdbcType=INTEGER},
#{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp}
)}
</select>
<resultMap type="com.atguigu.mybatis.bean.Employee" id="PageEmp">
<id column="EMPLOYEE_ID" property="id"/>
<result column="LAST_NAME" property="email"/>
<result column="EMAIL" property="email"/>
</resultMap>
代码调用的实现
@Test
public void testProcedure() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
OraclePage page = new OraclePage();
page.setStart(1);
page.setEnd(5);
mapper.getPageByProcedure(page);
System.out.println("总记录数:"+page.getCount());
System.out.println("查出的数据:"+page.getEmps().size());
System.out.println("查出的数据:"+page.getEmps());
}finally{
openSession.close();
}
}
十 SpringBoot集成MyBatisPlus
10.1 新建SpringBoot项目
代码地址:https://download.csdn.net/download/qq_33417321/88237864?spm=1001.2014.3001.5501
注意:这里Java版本选择8
springboot版本为2.3.7RELEACE
10.2、添加依赖
添加SpringBoot web依赖(把默认生成的spring-boot-starter改为spring-boot-starter-web)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
10.3、配置application.yml文件
server:
port: 8090 #服务端口
spring:
application:
name: payment-demo # 应用名称
10.4、创建controller
创建controller包,创建ProductController类
package com.maltose.paymentdemo.controller;
@CrossOrigin //开放前端的跨域访问
@RestController
@RequestMapping("/api/product")
public class ProductController {
@GetMapping("/test")
public String test(){
return "hello";
}
}
10.5、测试
访问:http://localhost:8090/api/product/test
10.6 引入MyBatisPlus依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--持久层-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
10.7、配置数据库连接
spring:
datasource: #mysql数据库连接,如果使用的mysql版本是5.x,则此处不需要写cj了(这里mysql版本是8.x)
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/payment_demo?serverTimezone=GMT%2B8&characterEncoding=utf-8
username: root
password: 123456
定义实体类
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//退款
@Data
@TableName("t_refund_info")
public class RefundInfo extends BaseEntity{
private String orderNo;//商品订单编号
private String refundNo;//退款单编号
private String refundId;//支付系统退款单号
private Integer totalFee;//原订单金额(分)
private Integer refund;//退款金额(分)
private String reason;//退款原因
private String refundStatus;//退款单状态
private String contentReturn;//申请退款返回参数
private String contentNotify;//退款结果通知参数
}
10.8、定义持久层
定义Mapper接口继承 BaseMapper<>,定义xml配置文件
例如OrderInfoMapper.java接口与OrderInfoMapper.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.paymentdemo.mapper.OrderInfoMapper">
</mapper>
package com.atguigu.paymentdemo.mapper;
import com.atguigu.paymentdemo.entity.OrderInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface OrderInfoMapper extends BaseMapper<OrderInfo> {
}
10.9、定义MyBatis-Plus的配置文件
在config包中创建配置文件 MybatisPlusConfig,用来扫描mybatis的mapper目录文件
package com.maltose.paymentdemo.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
//持久层扫描
@MapperScan("com.atguigu.paymentdemo.mapper")
//启用事务管理
@EnableTransactionManagement
public class MyBatisPlusConfig {
}
10.10、定义yml配置文件
添加持久层日志和xml文件位置的配置
mybatis-plus:
configuration: #sql日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:com/atguigu/paymentdemo/mapper/xml/*.xml
10.11、定义业务层
定义业务层接口继承 IService<>,定义业务层接口的实现类,并继承 ServiceImpl<,>
例如OrderInfoService与OrderInfoServiceImpl如下
package com.maltose.paymentdemo.service;
import com.maltose.paymentdemo.entity.OrderInfo;
import com.baomidou.mybatisplus.extension.service.IService;
public interface OrderInfoService extends IService<OrderInfo> {
}
package com.maltose.paymentdemo.service.impl;
import com.maltose.paymentdemo.entity.OrderInfo;
import com.maltose.paymentdemo.mapper.OrderInfoMapper;
import com.maltose.paymentdemo.service.OrderInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
}
10.12、定义接口方法查询所有商品
在 public class ProductController 中添加一个方法
@Resource
private ProductService productService;
@ApiOperation("商品列表")
@GetMapping("/list")
public R list(){
List<Product> list = productService.list();
return R.ok().data("productList", list);
}
10.13、pom中配置build节点
因为maven工程在默认情况下 src/main/java 目录下的所有资源文件是不发布到 target 目录下的,我们在 pom 文件的 节点下配置一个资源发布过滤器
<build>
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<!--默认是true-->
<filtering>false</filtering>
</resource>
</resources>
</build>