一.什么是Mybatis
1.MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。
2.什么是ORM框架:ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理
二.MyBatis入门学习
1.搭建环境
a.创建Maven工程,引入依赖
<dependencies>
<!-- MyBatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
b.创建MyBatis核心配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型 -->
<transactionManager
type="JDBC"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver"
value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql:///mybatis"/>
<property name="username"
value="root"/>
<property name="password"
value="root"/>
</dataSource>
</environment>
</environments>
</configuration>
c.将log4j.properties文件放入resources中,让控制台打印SQL语句。
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
#log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%d{MM/dd HH:mm:ss}] %-6r [%15.15t] %-5p %30.30c %x - %m\n
d.创建实体类
package org.example.pojo;
public class User {
private int id;
private String username;
private String sex;
private String address;
public User() {
}
public User(int id, String username, String sex, String address) {
this.id = id;
this.username = username;
this.sex = sex;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
e.在java目录创建持久层接口
package org.example.mapper;
import org.example.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> findAll();
}
f.在resource目录创建映射文件(注意文件夹路径)
<?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="org.example.mapper.UserMapper">
<select id="findAll"
resultType="org.example.pojo.User">
select * from user
</select>
</mapper>
g.将映射文件配置到mybatis核心配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<environment id="mysql">
<!-- 事务类型 -->
<transactionManager
type="JDBC"></transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver"
value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql:///mybatis"/>
<property name="username"
value="root"/>
<property name="password"
value="root"/>
</dataSource>
</environment>
</environments>
<!-- 注册映射文件-->
<mappers>
<mapper resource="org/example/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
注意事项:映射文件要和接口名称相同
映射文件要和接口的目录结构相同
映射文件中namespace属性要写接口的全名
映射文件中标签的id属性是接口方法的方法名
映射文件中标签的resultType属性是接口方法的返回值类型
映射文件中标签的parameterType属性是接口方法的参数类型
映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛 型的全类名
h.测试持久层接口方法
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.example.mapper.UserMapper;
import org.example.pojo.User;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
public class TestUserMapper {
@Test
public void testFindAll() throws Exception {
//1.读取核心配置文件
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3.SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory=builder.build(is);
//4.SqlSessionFactory对象获取SqlSession对象
SqlSession session=factory.openSession();
//5.SqlSession对象获取代理对象
UserMapper userMapper=session.getMapper(UserMapper.class);
//6.代理对象执行方法
List<User> all= userMapper.findAll();
all.forEach(System.out::println);
//7.释放资源
session.close();
is.close();
}
}
三.MyBatis核心对象及工作流程
MyBatis核心对象:
1.SqlSessionFactoryBuilder
SqlSession工厂构建者对象, 使用构造者模式创建SqlSession工厂对象
2.SqlSessionFactory
SqlSession工厂,使用使用工厂模式创建SqlSession对象
3.SqlSession
该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库
4.Mapper
持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库
MyBatis工作流程:
1.创建SqlSessionFactoryBuilder对象
2.SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
3.SqlSessionFactory对象生产了SqlSession对象:工厂模式
4.SqlSession对象创建了持久层接口的代理对象:动态代理模式
5.代理对象操作数据库
四.Mapper动态代理原理
1.SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类
型就是传入的接口类型
2.MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层
调用的是SqlSession的方法
五.CRUD
1.持久层接口添加方法
2.映射文件添加标签
3.测试方法
新增:
void add(User user);
<insert id="add" parameterType="org.example.pojo.User">
insert into user(username,sex,address)
values(#{username},#{sex},#{address})
</insert>
public void testAdd() throws IOException {
//1.读取核心配置文件
InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3.SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory=builder.build(is);
//4.SqlSessionFactory对象获取SqlSession对象
SqlSession session=factory.openSession();
//5.SqlSession对象获取代理对象
UserMapper userMapper=session.getMapper(UserMapper.class);
//6.代理对象执行方法
User user=new User("全栈工程师","男","上海");
userMapper.add(user);
session.commit();
session.close();
is.close();
}
修改:
利用Junit的前置后置方法,优化测试类代码。
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.example.mapper.UserMapper;
import org.example.pojo.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestUserMapper {
InputStream is = null;
SqlSession session = null;
UserMapper userMapper = null;
@Before
public void before() throws IOException {
//1.读取核心配置文件
is= Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3.SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory=builder.build(is);
//4.SqlSessionFactory对象获取SqlSession对象
session=factory.openSession();
//5.SqlSession对象获取代理对象
userMapper=session.getMapper(UserMapper.class);
}
@After
public void after() throws IOException {
//7.释放资源
session.close();
is.close();
}
@Test
public void testFindAll() {
//6.代理对象执行方法
List<User> all= userMapper.findAll();
all.forEach(System.out::println);
}
@Test
public void testAdd(){
User user=new User("全栈工程师","男","上海");
userMapper.add(user);
session.commit();
}
}
void update(User user);
<update id="update" parameterType="org.example.pojo.User">
update user
set username=#{username},
sex=#{sex},
address=#{address}
where id=#{id}
</update>
public void testUpdate(){
User user=new User(9,"前端工程师","女","上海");
userMapper.update(user);
session.commit();
}
删除:
void delete(int userId);//根据Id删除
<delete id="delete" parameterType="int">
delete from user where id=#{id}
</delete>
public void testDelete(){
userMapper.delete(6);
session.commit();
}
查询:
List<User> findAll();
<select id="findAll" resultType="org.example.pojo.User">
select * from user
</select>
public void testFindAll() {
//6.代理对象执行方法
List<User> all= userMapper.findAll();
all.forEach(System.out::println);
}
六.模糊查询
List<User> findByNameLike(String username);
<select id="findByNameLike" parameterType="string" resultType="org.example.pojo.User">
select * from user where username like #{username}
</select>
public void testFindByNameLike(){
List<User> users=userMapper.findByNameLike("%工程师%");
for(User user:users){
System.out.println(user);
}
}
1.模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:
<select id="findByNameLike" parameterType="string" resultType="org.example.pojo.User">
select * from user where username like '%${value}%'
</select>
2.使用<bind>定义参数:
<select id="findByNameLike" parameterType="string" resultType="org.example.pojo.User">
<bind name="likeName" value="'%'+username+'%'"/>
select * from user where username like #{likeName}
</select>
七.分页查询
页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:
1.顺序传参(可读性低,开发中不建议使用):
List<User> findPage(int startIndex,int pageSize);
<select id="findPage" resultType="org.example.pojo.User">
select * from user limit #{arg0},#{arg1}
</select>
<select id="findPage" resultType="org.example.pojo.User">
select * from user limit #{param1},#{param2}
</select>
public void testFindPage(){
List<User> users=userMapper.findPage(0,3);
users.forEach(System.out::println);
}
2.@Param传参
在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。
List<User> findPage1(@Param("startIndex")int starIndex,@Param("pageSize")int pageSize);
<select id="findPage1" resultType="org.example.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>
public void testFindPage1(){
List<User> users=userMapper.findPage1(3,3);
users.forEach(System.out::println);
}
3.POJO传参
package org.example.pojo;
public class PageQuery {
private int startIndex;
private int pageSize;
public PageQuery() {
}
public PageQuery(int startIndex, int pageSize) {
this.startIndex = startIndex;
this.pageSize = pageSize;
}
public int getStartIndex() {
return startIndex;
}
public void setStartIndex(int startIndex) {
this.startIndex = startIndex;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
List<User> findPage2(PageQuery pageQuery);
<select id="findPage2" resultType="org.example.pojo.User" parameterType="org.example.pojo.PageQuery">
select * from user limit #{startIndex},#{pageSize}
</select>
public void testFindPage2(){
PageQuery pageQuery=new PageQuery(3,4);
List<User> users=userMapper.findPage2(pageQuery);
users.forEach(System.out::println);
}
4.Map传参
List<User> findPage3(Map<String,Object> params);
<select id="findPage3" resultType="org.example.pojo.User" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>
public void testFindPage3(){
Map<String,Object> params = new HashMap();
params.put("startIndex",0);
params.put("pageSize",4);
List<User> users = userMapper.findPage3(params);
users.forEach(System.out::println);
}
八.MyBatis聚合查询
1.主键回填:有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。
void add(User user);
<insert id="add" parameterType="org.example.pojo.User">
<!-- KeyProperty:主键属性名,KeyColumn:主键列表,resultType:主键类型,order:执行时机-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID();
</selectKey>
insert into user(username,sex,address)
values(#{username},#{sex},#{address})
</insert>
public void testAdd(){
User user=new User("全栈工程师","男","北京");
System.out.println(user);
userMapper.add(user);
session.commit();
System.out.println(user);
}
SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。
int findCount();
<select id="findCount" resultType="int">
select count(id) from user
</select>
public void testFindCount(){
System.out.println(userMapper.findCount());
}
九.MyBatis配置文件
1.properties
属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。
我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境 -->
<properties resource="db.properties"></properties>
<environments default="mysql">
<environment id="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>
<mapper resource="org/example/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
2.settings
该标签是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。
3.plugins
该标签是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。
4.typeAliases
MyBatis对常用类有默认别名支持。
为一个类配置别名:
<typeAliases>
<typeAlias type="全类名" alias="别名">
</typeAlias>
</typeAliases>
为一个所有包下的所有类配置别名:
<typeAliases>
<package name="包名"></package>
</typeAliases>
5.environments
<environments> 可以为MyBatis配置数据环境。
事务管理:
<environments default="mysql">
<environment id="mysql">
<!-- JDBC:使用JDBC的提交和回滚
MANAGED:不做事务处理-->
<transactionManager type="JDBC">
</transactionManager>
</environment>
</environments>
连接池:
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC">
</transactionManager>
<!-- 连接池设置 -->
<dataSource type="POOLED">
<!-- 数据源设置... -->
</dataSource>
</environment>
</environments>
dataSource的type属性:
POOLED:使用连接池管理连接,使用MyBatis自带的连接池。
UNPOOLED:不使用连接池,直接由JDBC连接。
JNDI:由JAVAEE服务器管理连接,如果使用Tomcat作为服务器则使用Tomcat自带的连接池
管理。
6.mappers
<mappers> 用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:
使用相对路径注册:
<mappers>
<mapper
resource="org/example/mapper/UserMapper.xml"/>
</mappers>
使用绝对路径注册映射文件:
<mappers>
<mapper
url="file:///C:\Users\a\IdeaProjects\mybat
iscase\mybatisDemo1\src\main\resources\org
\example\mapper\UserMapper.xml"/>
</mappers>
注册持久层接口:
<mappers>
<mapper
class="org.example.mapper.UserMapper"/>
</mappers>
注册一个包下的所有持久层接口:
<mappers>
<package name="org.example.mapper"/>
</mappers>
十.MyBatis映射文件
1.resultMap
MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同。
当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。
2.<sql><include>
<sql>可以用来重用Sql片段,通过<include>引入该片段。
以下示例:
<sql id="selectAllField">
select tid as id,tname as teacherName
</sql>
<select id="findAll" resultType="org.example.pojo.Teacher">
<include refid="selectAllField">
</include>
from teacher;
</select>
public void testFindAll(){
List<Teacher> all=teacherMapper.findAll();
all.forEach(System.out::println);
}
3.特殊字符处理
在Mybatis映射文件中尽量不要使用一些特殊字符< >等.可以使用符号的实体来表示。
<select id="findById2" resultMap="org.example.pojo.Teacher">
<include refid="selectAllField"></include>
form teacher where tid $gt;#{id}
</select>
十一.动态Sql
1.<if>
一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商
品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此
时就需要在方法中使用动态Sql语句。
<if>标签内Sql片段在满足条件后才会添加,用法为:<if test="条件">
十二.缓存
十三.关联查询
十四.注解开发
1.环境搭建
MyBatis可以使用注解替代映射文件,这样就不需要使用映射文件了,直接在持久层接口上写Sql语句,使用@Select/@Delete/@Insert/@Update定义Sql语句。
首先创建持久层接口
package org.example.mapper;
import org.apache.ibatis.annotations.Select;
import org.example.pojo.User;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> findAll();
}
需要在核心配置文件中注册
<!-- 注册接口-->
<mappers>
<package name="org.example.mapper"/>
</mappers>
测试方法成功
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.example.mapper.UserMapper;
import org.example.pojo.User;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestUserMapper {
InputStream is = null;
SqlSession session = null;
UserMapper userMapper = null;
@Before
public void before() throws IOException {
//1.读取核心配置文件
is= Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3.SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
SqlSessionFactory factory=builder.build(is);
//4.SqlSessionFactory对象获取SqlSession对象
session=factory.openSession();
//5.SqlSession对象获取代理对象
userMapper=session.getMapper(UserMapper.class);
}
@After
public void after() throws IOException {
//7.释放资源
session.close();
is.close();
}
@Test
public void testFindAll() {
//6.代理对象执行方法
List<User> all= userMapper.findAll();
all.forEach(System.out::println);
}
}
2.注解开发CRUD
与映射开发几乎没有差别
3.映射开发和注解开发比较
MyBatis中更推荐使用映射文件开发,Spring、SpringBoot更推荐注解方式。具体使用要视项目情况而定。他们各自的优点如下:
1.映射文件
2.注解开发