MyBatis框架

什么是框架

框架即是一个半成品软件。

​ 开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。

​ 如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修

使用框架开发的好处:

  1. 省去大量的代码编写、减少开发时间、降低开发难度。
  2. 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
  3. 将程序员的注意力从技术中抽离出来,更集中在业务层面。

什么是ORM框架?

ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。

之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数据转换。

// 新增方法,将对象转为sql语句字段

public void AddUser(User user) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");

String sql = "INSERT INTO user values (null,?,?,?,?)";


PreparedStatement preparedStatement = connection.prepareStatement(sql);

preparedStatement.setString(1,user.getName());

preparedStatement.setInt(2,user.getAge());

preparedStatement.setString(3,user.getAddress());

preparedStatement.setString(4,user.getSex());

preparedStatement.executeUpdate();

// 省略资源关闭...

}



// 查询方法,将数据库结果集转为对象

public List<User> findAllUser() throws Exception {

Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");

PreparedStatement preparedStatement = connection.prepareStatement("select * from user");

ResultSet resultSet = preparedStatement.executeQuery();


//遍历查询结果集

List<User> users = new ArrayList<>();

while(resultSet.next()){

// 拿到每一列数据

int id = resultSet.getInt("id");

String name = resultSet.getString("name");

int age = resultSet.getInt("age");

String address = resultSet.getString("address");

String sex = resultSet.getString("sex");

// 将数据封装到对象中
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
user.setAddress(address);
user.setSex(sex);
users.add(user);
}
// 省略资源关闭...
return users;}

这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。

而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。

什么是MyBatis?

image-20220402090018524

MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。

补充:

Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。MyBatis与Hibernate的比较:

  • MyBatis是一个半自动的ORM框架,需要手写SQL语句。
  • Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
  • 使用MyBatis的开发量要大于Hibernate。

为什么Hibernate市场占有率越来越低:

  • 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
  • Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
  • 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
  • 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。

MyBatis入门

image-20220402100111727

环境搭建:

  1. 将SQL文件导入数据库
  2. 创建maven工程,引入依赖
<?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>com.itbaizhan</groupId>
<artifactId>mybatisDemo1</artifactId>
<version>1.0-SNAPSHOT</version>


<!--必须引入的依赖有两个-->
<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>
</dependency>

<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>

</project>

3.创建mybatis核心配置文件SqlMapConfig.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>
<!-- 配置环境 -->
<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>

4.将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

5.创建实体类

package com.itbaizhan.pojo;

public class User {
private int id;
private String username;
private String sex;
private String 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 + '\'' +
'}';
}

public User(int id, String username, String sex, String address) {
this.id = id;
this.username = username;
this.sex = sex;
this.address = address;
}

public User() {
}
}

创建持久层接口和映射文件

  1. 在java目录创建持久层接口

    package com.itbaizhan.mapper;
    import com.itbaizhan.pojo.User;
    import java.util.List;

    public interface UserMapper {
    List<User> findAll();
    }

2.在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="com.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
</mapper>

3.将映射文件配置到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="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="com/itbaizhan/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>

映射文件注意事项:

  • 映射文件要和接口名称相同。

  • 映射文件要和接口的目录结构相同。

    image-20220402102933888

  • 映射文件中namespace属性要写接口的全名。

  • 映射文件中标签的id属性是接口方法的方法名

  • 映射文件中标签的resultType属性是接口方法的返回值类型

  • 映射文件中标签的parameterType属性是接口方法的参数类型

  • 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。

测试持久层接口方法

image-20220402201101710

import com.itbaizhan.mapper.UserMapper;
import com.itbaizhan.pojo.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TestUserMapper {
@Test
public void testFindAll() 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 sesison = factory.openSession();
//(5)SqlSession对象获取代理对象
UserMapper mapper = sesison.getMapper(UserMapper.class);
//(6)代理对象执行方法
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user.toString());
}
//(7) 释放资源
sesison.close();
is.close();
}
}

MyBatis核心对象以及工作流程

image-20220402204856384

MyBatis核心对象

SqlSessionFactoryBuilder

SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。

SqlSessionFactory

SqlSession工厂,使用工厂模式创建SqlSession对象。

SqlSession

该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。

Mapper

持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库

MyBatis工作流程

  1. 创建SqlSessionFactoryBuilder对象
  2. SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
  3. SqlSessionFactory对象生产了SqlSession对象:工厂模式
  4. SqlSession对象创建了持久层接口的代理对象:动态代理模式
  5. 代理对象操作数据库

使用SqlSession操作数据库

image-20220402211818633

除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:

@Test
public void testFindAll2() 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 sesison = factory.openSession();
//SqlSession对象直接操纵数据库
//持久层接口全类名+方法名
List<Object> users = sesison.selectList("com.itbaizhan.mapper.UserMapper.findAll");
for (Object user : users) {
System.out.println(user.toString());
}
//(6)关闭资源
sesison.close();
is.close();
}

Mapper动态代理原理

image-20220402212625287

接下来我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。

获取代理对象

点开测试类的getMapper方法,查看该方法最终调用了什么方法。

image-20211019162901115

当看到Proxy.newProxyInstance时,可以确定getMapper方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式

查看代理方式

点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。

image-20211019163829272

可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。

结论:

  • SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
  • MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。

MyBatis新增

新增用户

1.持久层接口添加方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);

}

2.映射文件添加标签

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address}))
</insert>
</mapper>

3.编写测试方法

@Test
public void testAdd() throws Exception
{
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
SqlSession session = factory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User("程序员", "男", "上海");
userMapper.add(user);
//提交事务(增删改)在MyBatis中事务提交是默认关闭的
//提交事务(增删改)再MyBatis中事务提交是默认关闭的
//在MyBatis中,执行查询操作后不需要手动提交事务
session.commit();
session.close();
is.close();

}

注意:

1.当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用#{POJO的属性名}即可

2.MyBatis事务默认手动提交,所以在执行完增删改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。

MyBatis修改

优化测试类

我们发现MyBatis的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用Junit的前置与后置方法,优化测试类代码。

public class TestUserMapper2 {
InputStream is = null;
SqlSession session = null;
UserMapper userMapper = null;
@Before
public void before() throws IOException {
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 {
//释放资源
session.close();
is.close();

}

修改用户

1.持久层接口添加方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
}

2.映射文件添加标签

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}

update user set username = #{username},sex = #{sex},address=#{address} where id = #{id}
</update>
</mapper>

3.编写测试方法

@Test
public void testUpdate()
{
User user = new User(1,"程序员1","女","深圳");
userMapper.update(user);
session.commit();
}

MyBatis删除、根据ID查询

删除用户

1.持久层接口添加方法

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);

}

2.映射文件添加标签

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
delete from user where id = #{id}
</delete>
</mapper>

注意:

当方法的参数类型是简单数据类型时,#{}中可以写任意名称

  • 简单数据类型:基本数据类型、字符串等

3.编写测试方法

@Test
public void testDelete()
{
userMapper.delete(7);
session.commit();
}

根据ID查询用户

1.持久层接口添加方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);
}

2.映射文件添加标签

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
select * from user where id = #{userid}
select * from user where id = #{userid}
</select>
</mapper>

3.编写测试方法

@Test
public void testFindById()
{
User user = userMapper.findById(1);
System.out.println(user);
}

MyBatis模糊查询

使用#定义参数

1.持久层接口添加方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);

List<User> findByUsernameLike(String username);
List<User> findByUsernameLike(String username);
}

2.映射文件添加标签

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
select * from user where id = #{userid}
</select>
<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
select * from user where username like #{user}
select * from user where username like #{user}
</select>
</mapper>

3.编写测试方法

@Test
public void testFindByNameLike(){
List<User> users = userMapper.findByUsernameLike("%尚学堂%");
List<User> users = userMapper.findByUsernameLike("%尚学堂%");
users.forEach(System.out::println);
}

我们看到在映射文件中,parameterType的值为string而没有写java.lang.String,这是为什么呢?

参数/返回值类型为基本数据类型/包装类/String等类型时,我们可以写全类名,也可以写别名

数据类型 别名
byte _byte
long _long
short _short
int _int
int _integer
double _double
float _float
boolean _boolean
String string
Byte byte
Long long
Short short
Integer int/integer
Double double
Float float
Boolean boolean
Date date
BigDecimal decimal/bigdecimal
Object object
Map map
HashMap hashmap
List list
ArrayList arraylist
Collection collection
Iterator iterator

使用$定义参数

模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
select * from user where username like '%${value}%'
select * from user where username like '%${value}%'
</select>

测试方法写法如下:

@Test

public void testFindByNameLike(){

List<User> users = userMapper.findByUsernameLike("尚学堂");

users.forEach(System.out::println);

}

#和$的区别:

  1. #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
  2. #可以防止sql注入,一般能用#就不用$。
  3. #可以防止sql注入,一般能用#就不用$
  4. ${}内部的参数名必须写value。

使用定义参数

如果使用#还不想在调用方法的参数中添加%,可以使用<bind><bind>允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的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">
<mapper namespace="com.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
select * from user where id = #{userid}
</select>
<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like #{user}-->
<!-- </select>-->

<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like '%${value}%'-->
<!-- </select>-->

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
<bind name="likeName" value="'%'+username+'%'"/>
<bind name="likeName" value="'%'+username+'%'">
select * from user where username like #{likeName}
select * from user where username like #{likeName}
</select>
</mapper>

测试方法写法如下;

@Test
public void testFindByNameLike(){
List<User> users = userMapper.findByUsernameLike("尚学堂");
users.forEach(System.out::println);
}

MyBatis分页查询

image-20220403110555397

分页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:

顺序传参:

Sql中的参数使用arg0,arg1…或param1,param2…表示参数的顺序。此方法可读性较低,在开发中不建议使用。

1.持久层接口方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);

List<User> findByUsernameLike(String username);


/**
* 分页查询
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage(int startIndex,int pageSize);
List<User> findPage(int startIndex,int pageSize);

}

2.映射文件



<select id="findPage" resultType="com.itbaizhan.pojo.User">
select * from user limit #{arg0},#{arg1}
</select>

<select id="findPage" resultType="com.itbaizhan.pojo.User">
select * from user limit #{param1},#{param2}
selecy * from user limit #{param1},#{param2}
</select>



3.测试类

@Test
public void testFindPage()
{
List<User> users = userMapper.findPage(0,3);
users.forEach(System.out::println);
}

@Param传参

在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。

1.持久层接口方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);

List<User> findByUsernameLike(String username);


/**
* 分页查询
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage(int startIndex,int pageSize);

/**
* 分页查询1
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);
}

2.映射文件

<select id="findPage1" resultType="com.itbaizhan.pojo.User">
select * from user limit #{startIndex},#{pageSize}
select * from user limit #{startIndex},#{pageSize}
</select>

3.测试类

@Test
public void testFindPage1()
{
List<User> users = userMapper.findPage1(3,3);
users.forEach(System.out::println);
}

MyBatis映射文件的Sql语句中表示接口方法的第一个参数可以写成:

#{arg0}或#{param1}

MyBatis接口方法的参数前使用,定义映射文件中Sql语句的参数名:

@Param

POJO传参

自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

1.自定义POJO类

package com.itbaizhan.pojo;

public class PageQuery {
private int startIndex;
private int 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;
}

public PageQuery() {
}

public PageQuery(int startIndex, int pageSize) {
this.startIndex = startIndex;
this.pageSize = pageSize;
}
}

2.持久层接口方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.PageQuery;
import com.itbaizhan.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);

List<User> findByUsernameLike(String username);


/**
* 分页查询
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage(int startIndex,int pageSize);

/**
* 分页查询1
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);

//分页查询2
List<User> findPage2(PageQuery pageQuery);


}

3.映射文件

<select id="findPage2" resultType="com.itbaizhan.pojo.User" parameterType="com.itbaizhan.pojo.PageQuery">
2
select * from user limit #{startIndex},#{pageSize}
3
</select>

4.测试类

@Test
public void testFindPage2()
{
PageQuery pageQuery = new PageQuery(3, 3);
List<User> users = userMapper.findPage2(pageQuery);
users.forEach(System.out::println);
}

Map传参

如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。

1.持久层接口方法

package com.itbaizhan.mapper;
import com.itbaizhan.pojo.PageQuery;
import com.itbaizhan.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface UserMapper {
List<User> findAll();
void add(User user);
void update(User user);
void delete(int userId);
User findById(int userId);

List<User> findByUsernameLike(String username);


/**
* 分页查询
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage(int startIndex,int pageSize);

/**
* 分页查询1
* @param startIndex 开始索引
* @param pageSize 每页条数
* @return
* */
List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize") int pageSize);

//分页查询2
List<User> findPage2(PageQuery pageQuery);

//分页查询3
List<User> findPage3(Map<String,Object> params);
List<User> findPage3(Map<String,Object> params);
}

2.映射文件

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
select * from user where id = #{userid}
</select>
<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like #{user}-->
<!-- </select>-->

<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like '%${value}%'-->
<!-- </select>-->

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
<bind name="likeName" value="'%'+username+'%'"/>
select * from user where username like #{likeName}
</select>

<!-- <select id="findPage" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user limit #{arg0},#{arg1}-->
<!-- </select>-->

<select id="findPage" resultType="com.itbaizhan.pojo.User">
select * from user limit #{param1},#{param2}
</select>

<select id="findPage1" resultType="com.itbaizhan.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>

<select id="findPage2" resultType="com.itbaizhan.pojo.User" parameterType="com.itbaizhan.pojo.PageQuery">
select * from user limit #{startIndex},#{pageSize}
</select>

<select id="findPage3" resultType="com.itbaizhan.pojo.User" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>

</mapper>

3.测试类

@Test
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中,能作为参数载体的是:

Map

MyBatis聚合查询、主键回填

查询用户总数

1.持久层方法

//查询用户总数
int findCount();

2.映射文件

<select id="findCount" resultType="int">
select count(id) from user
</select>

3.测试类

@Test
public void testFindCount()
{
int count = userMapper.findCount();
System.out.println(count);
}

主键回填

有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。

1.持久层接口方法

//主键回填功能(运用在主键自增的情况下)
void add2(User user);

2.映射文件

<?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.itbaizhan.mapper.UserMapper">
<!-- id对应的是接口中的方法名 -->
<!-- resultType对应的是接口方法的参数类型 -->
<select id="findAll" resultType="com.itbaizhan.pojo.User">
select * from user
</select>
<insert id="add" parameterType="com.itbaizhan.pojo.User">
insert into user (username,sex,address) values (#{username},#{sex},#{address})
</insert>
<update id="update" parameterType="com.itbaizhan.pojo.User">
update user set username = #{username},sex = #{sex},address=#{address}
where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
select * from user where id = #{userid}
</select>
<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like #{user}-->
<!-- </select>-->

<!-- <select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user where username like '%${value}%'-->
<!-- </select>-->

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
<bind name="likeName" value="'%'+username+'%'"/>
select * from user where username like #{likeName}
</select>

<!-- <select id="findPage" resultType="com.itbaizhan.pojo.User">-->
<!-- select * from user limit #{arg0},#{arg1}-->
<!-- </select>-->

<select id="findPage" resultType="com.itbaizhan.pojo.User">
select * from user limit #{param1},#{param2}
</select>

<select id="findPage1" resultType="com.itbaizhan.pojo.User">
select * from user limit #{startIndex},#{pageSize}
</select>

<select id="findPage2" resultType="com.itbaizhan.pojo.User" parameterType="com.itbaizhan.pojo.PageQuery">
select * from user limit #{startIndex},#{pageSize}
</select>

<select id="findPage3" resultType="com.itbaizhan.pojo.User" parameterType="map">
select * from user limit #{startIndex},#{pageSize}
</select>

<select id="findCount" resultType="int">
select count(id) from user
</select>

<insert id="add2" parameterType="com.itbaizhan.pojo.User">
<!--keyProperty:主键属性名 keyColumn:主键列名 resultType:主键类型 order:执行-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID();
//SELECT LAST_INSERT_ID();
</selectKey>
insert into user(username, sex, address)
VALUES (#{username},#{sex},#{address})
</insert>

<insert id="add2" parameterType="com.itbaizhan.pojo.User">
<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>
</mapper>

SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。

3.测试类

@Test
public void testAdd2()
{
User user = new User("程序猿", "男", "北京");
userMapper.add2(user);
session.commit();
System.out.println(user);
}

MyBatis配置文件_

image-20220403231936003

MyBatis配置文件结构:

-configuration

-properties(属性)

-property

-settings(全局配置参数)

-setting

-plugins(插件)

-plugin

-typeAliases(别名)

-typeAliase

-package

-environments(环境)

-environment

-transactionManager(事务管理)
-dataSource(数据源)

-mappers(映射器)

-mapper

-package

properties

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。

例如:我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。

1.编写db.properties

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql:///mybatis
jdbc.username = root
jdbc.password = root

2.在配置文件中引入db.properties

<?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>
<!-- 通过引入外部的配置文件可以动态修改配置,不需要修改核心配置文件,利用${name} -->
<properties resource="db.properties"></properties>
<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="com/itbaizhan/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>

当然我们也可以将数据源数据通过<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>
<!-- 通过引入外部的配置文件可以动态修改配置,不需要修改核心配置文件,利用${name} -->
<properties resource="db.properties"></properties>
<!-- 可以有以下的写法,不过没有意义 -->
<!-- <properties>-->
<!-- <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"/>-->
<!-- </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="com/itbaizhan/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>

在MyBatis配置文件中,引入外部文件的属性为:resource

MyBatis配置文件_

<settings>是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。

MyBatis配置文件_

<plugins>是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

MyBatis配置文件_

image-20220403233518087

MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用<typeAliases>设置自定义别名。

为一个类配置别名

1.配置文件

<typeAliases>

<typeAlias type="全类名" alias="别名"></typeAlias>

</typeAliases>
<typeAliases>
<!--type:全类名 alias:别名-->
<typeAlias type="com.itbaizhan.pojo.User" alias="user"></typeAlias>
<!--为该包下的所有类配置别名,别名省略包名,和类型相同-->
<package name="com.itbaizhan.pojo"/>
</typeAliases>

2.映射文件

<select id="findAll" resultType="user">
select * from user
</select>

为一个所有包下的所有类配置别名

<typeAliases>

<package name="包名"></package>

</typeAliases>

此时该包下的所有类都有了别名,别名省略包名,和类名相同。如:

1.配置文件:

<typeAliases>
<!--type:全类名 alias:别名-->
<typeAlias type="com.itbaizhan.pojo.User" alias="user"></typeAlias>
<!--为该包下的所有类配置别名,别名省略包名,和类型相同-->
<package name="com.itbaizhan.pojo"/>
</typeAliases>

2.映射文件:

<select id="findPage2" resultType="User" parameterType="PageQuery">
select * from user limit #{startIndex},#{pageSize}
</select>

MyBatis配置文件_

image-20220403234207474

<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自带的连接池管理

MyBatis配置文件_

<mappers>用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:

    <mappers>
<!-- 相对路径注册映射文件 -->
<mapper resource="com/itbaizhan/mapper/UserMapper.xml"></mapper>
<!-- 绝对路径注册映射文件 -->
<mapper url="file:///E:\Eclipse_workspace1\MyBatisCase\mybatisDemo1\src\main\resources\com\itbaizhan\mapper\UserMapper.xml"></mapper>
<!-- 注册持久层接口 -->
<mapper class="com.itbaizhan.mapper.UserMapper"></mapper>
<!-- 注册一个包下的所有持久层接口 -->
<package name="com.itbaizhan.mapper"/>
</mappers>

MyBatis映射文件_

image-20220404134300155

MyBatis映射文件中除了<insert><delete><update><select>外,还有一些标签可以使用:

resultMap

标签的作用的自定义映射关系。

MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:

MyBaits可以将数据库结果集封装到对象中,是因为结果集的列名和对面属性名相同;

image-20211103144440901

当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。如:

image-20211103144728224

这个时候有两种解决方案:

1.Sql语句的查询字段起与POJO属性相同的别名。

<?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.itbaizhan.mapper.TeacherMapper">
<select id="findAll" resultType="com.itbaizhan.pojo.Teacher">
select tid as id, tname as teacherName from teacher-->
</select>

</mapper>

2.自定义映射关系、

在映射文件中,使用<resultMap>自定义映射关系:


<!-- id:自定义映射名 type:自定义映射的对象类型 -->
<resultMap id="teacherMapper" type="com.itbaizhan.pojo.Teacher">
<!-- id定义主键列 property:POJO属性名 column:数据库列名 -->
<id property="id" column="tid"></id>
<!-- result定义普通列 property:POJO属性名 column:数据库列名 -->
<result property="teacherName" column="tname"></result>
</resultMap>

<resultMap id ="teacherMapper" type="com.itbaizhan.pojo.Teacher">
<id property="id" column="tid"></id>
<result property="teacherName" column="tname"></result>
</resultMap>