Mybatis

Mybatis

API方法

使用Maven构建项目在pom.xml中导入

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>

在resources中配置数据库连接的配置

注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
//和我们平常配置数据连接一模一样
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java196"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>

创建实体类(StuEntity)

package com.entity;

public class StuEntity {

private Integer myid;
private String username;

public Integer getMyid() {
return myid;
}

public void setMyid(int myid) {
this.myid = myid;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

@Override
public String toString() {
return "stu{" +
"myid=" + myid +
", username='" + username + '\'' +
'}';
}
}

写sql语句查询数据

这个查询语句是在xml文件中写,resoures下,这个文件统称为映射文件

<?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="constituent">
<select id="stu" resultType="com.entity.StuEntity">
select * from stu
</select>
</mapper>

创建一个测试Main

public class Main {
public static void main(String[] args) throws Exception {
String res="mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(res);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

try (SqlSession session = sqlSessionFactory.openSession()) {
List<StuEntity> StuEntity = session.selectList("constituent.stu");
System.out.println("stu = " + StuEntity);
}
}
}

mapper方法

mapper 方法的主要用途:

  1. 定义数据库操作mapper 方法定义了具体的数据库操作,比如查询、插入、更新和删除等。每个方法对应一个 SQL 语句,通过 MyBatis 的映射机制,这些方法可以直接与数据库交互。

  2. 自动映射:MyBatis 会根据方法名和返回类型,自动将数据库查询结果映射为相应的 Java 对象。这使得开发者可以专注于业务逻辑,而不必处理复杂的结果集转换。

  3. 简化开发:通过 mapper,开发者可以避免直接编写繁杂的 JDBC 代码,大大简化了数据访问层的开发工作,提高了开发效率和代码的可维护性。

首先创建一个Dao文件夹

创建一个接口类StuMapper

public interface StuMapper {
StuEntity getStuById();
}

最主要的是sql的映射文件,命名空间要与mapper接口类型对应起来(接口的全称)

id要与mapper接口的方法对应起来即可

<mapper namespace="com.dao.StuMapper">
<select id="getStuById" resultType="com.entity.StuEntity">
select * from stu where myid=1
</select>
</mapper>

然后把Main中的

List<StuEntity> StuEntity = session.selectList("constituent.stu");
System.out.println("stu = " + StuEntity);

更改为

//接口+mapper文件
//要点1:getMapper
StuMapper stuMapper = session.getMapper(StuMapper.class);
StuEntity stu = stuMapper.getStuById();

常规写法

这是平常的规格写法,和上面的写法更改了一下结构,使用的方法是mapper方法+映射Sql语句方法

工具包

SqlSessionFactoryUtils

这个 SqlSessionFactoryUtils 类的目的是为 MyBatis 提供一个方便的工具类,用于获取 SqlSessionFactorySqlSession 对象,从而简化在应用程序中执行数据库操作的过程

public class SqlSessionFactoryUtils {

private static SqlSessionFactory sqlSessionFactory;

static {
String res = "mybatis-config.xml";
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}

public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
f static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}

}
1. 静态变量 sqlSessionFactory
private static SqlSessionFactory sqlSessionFactory;
  • 作用:这是一个静态变量,用于持有 SqlSessionFactory 实例。
  • 目的SqlSessionFactory 是 MyBatis 中的核心对象,负责创建 SqlSession 对象。通过将其设为静态变量,确保在整个应用程序的生命周期内只会创建一个 SqlSessionFactory 实例(即单例模式),提高性能和资源利用效率。
静态代码块
javaCopy codestatic {
String res = "mybatis-config.xml";
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}
  • ‘’作用:这个静态代码块在类加载时自动执行,并初始化 sqlSessionFactory

  • 步骤解释

    1. 指定配置文件

      String res = "mybatis-config.xml";
      • 定义了一个字符串变量 res,表示 MyBatis 配置文件的路径。
    2. 加载配置文件

      InputStream resourceAsStream = Resources.getResourceAsStream(res);
      • 使用 MyBatis 提供的 Resources.getResourceAsStream() 方法将配置文件加载为输入流 (InputStream)。
      • 如果加载失败,会抛出 IOException,并在 catch 块中将异常包装为运行时异常抛出。
    3. 创建 SqlSessionFactory

      sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
      • 使用 SqlSessionFactoryBuilderbuild() 方法根据配置文件创建 SqlSessionFactory 实例。
3. getSqlSessionFactory() 方法
javaCopy codepublic static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
  • 作用:提供一个公共的静态方法,用于返回已经初始化的 SqlSessionFactory 实例。
  • 目的:允许其他类通过这个方法获取 SqlSessionFactory,而不需要直接处理配置文件或构建过程。
4. getSqlSession() 方法
javaCopy codepublic static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
  • 作用:提供一个公共的静态方法,用于获取 SqlSession 对象。

    openSession(true)
    • true 参数表示自动提交事务。即每当执行完一个操作后,事务会自动提交,避免手动提交事务的麻烦。
  • 目的SqlSession 是 MyBatis 与数据库交互的核心对象,通过它可以执行 SQL 查询、插入、更新、删除等操作。这个方法简化了获取 SqlSession 的过程,便于快速执行数据库操作。

    总结
    • 静态初始化:通过静态代码块初始化 SqlSessionFactory,保证它只在类加载时创建一次。
    • 便捷访问:通过提供的静态方法 getSqlSessionFactory()getSqlSession(),其他类可以方便地获取 SqlSessionFactorySqlSession,简化数据库操作。
    • 自动提交getSqlSession() 方法默认返回自动提交的 SqlSession,减少事务管理的复杂性。

MapperFactory(BUG)

这里这个命名有问题所以说我改为了MapperFactory而不是MapperFactoryUtils ,其实可以加Utils

这里有个bug,由于银猫我还没学到关于如何关闭这个SqlSession 的生命周期,所以说代码能跑就行,后面再说

public class MappeFactory{
//这种写法对于seisson的关闭没有处理好
public static <T> T getMapper(Class<? extends T> mapperClass) {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSession();
T mapper = sqlSession.getMapper(mapperClass);
//不能这样写,写了后调用mapper的方法时是无法操作数据库,报executor已经关闭异常
//session.close();
return mapper;
}
}

业务类接口(StuService)

  • 接口的作用StuService 接口是业务逻辑层的一个抽象,定义了业务操作的合同。在这个例子中,getStuById() 方法用于获取学生信息。实现这个接口的类将包含实际的业务逻辑,而接口则为业务层与数据访问层的交互提供了一个松耦合的方式。
public interface StuService {
StuEntity getStuById();
}
业务类包(StuServicelmpl)
public class StuServicelmpl implements StuService {
@Override
public StuEntity getStuById() {
return MappeFactoryUtils.getMapper(StuMapper.class).getStuById();
}
}
  1. 实现 StuService 接口
    • StuServiceImpl 实现了 StuService 接口,提供了 getStuById() 方法的具体实现。这表示该类包含了学生相关的业务逻辑。
  2. 获取 Mapper 实例
    • MapperFactoryUtils.getMapper(StuMapper.class):通过 MapperFactoryUtils 获取 StuMapper 的实例。StuMapper 是一个 MyBatis Mapper 接口,负责与数据库进行交互。
  3. 调用 Mapper 方法
    • getStuById():调用 StuMapper 中定义的 getStuById() 方法,该方法从数据库中查询学生信息并返回 StuEntity 实体。

占位符#{}和${}的区别

在 MyBatis 中,#{}${} 都是用于向 SQL 语句中传递参数的占位符,但它们的处理方式和使用场景不同。

1. #{}:安全的参数传递(预编译方式)

  • 用途#{} 使用 预编译 的方式传递参数,参数会被作为占位符(?)传递给 JDBC 驱动。MyBatis 会在执行 SQL 时,将实际的参数值安全地绑定到这些占位符上。
  • 优点#{} 能有效防止 SQL 注入,因为参数被当作普通数据处理,不会直接拼接到 SQL 语句中。
  • 底层原理:MyBatis 会将 #{} 替换为 SQL 中的 ?,并使用 PreparedStatement 来安全地设置参数值。
<select id="getStuById" resultType="StuEntity">
SELECT * FROM students WHERE id = #{id}
</select>

如果传入 id123,最终生成的 SQL 语句类似于:

SELECT * FROM students WHERE id = ?

然后 MyBatis 会在执行时,将 id = 123 绑定到 ? 处。

  • 防止 SQL 注入:由于参数是通过 PreparedStatement 绑定的,SQL 注入风险较低。

2. ${}:直接文本替换(拼接方式)

  • 用途${} 会将参数的值直接拼接到 SQL 语句中,不做任何预处理。这类似于字符串拼接。

  • 风险:由于参数直接嵌入到 SQL 语句中,因此容易造成 SQL 注入 的风险。应谨慎使用。

  • 底层原理${} 会将传入的参数直接插入到 SQL 字符串中。

    <select id="getStudentsByTable" resultType="StuEntity">
    SELECT * FROM ${tableName} WHERE id = ${id}
    </select>

    如果传入的参数是 tableName = "students"id = 123,生成的 SQL 语句类似于:

    SELECT * FROM students WHERE id = 123

    但是如果传入的 tableName 是某些恶意输入(如 "students; DROP TABLE students"),则可能会造成 SQL 注入攻击。

    • 使用场景${} 一般用于动态生成 SQL 语句的一部分,如表名或列名。这些场景下无法使用 #{},因为 #{} 只能用于传递数据,而不能用于替换 SQL 语句中的结构性元素。

如何传递参数

在 MyBatis 中,传递参数是将 Java 层的参数传递给 SQL 语句的一种方式。MyBatis 提供了灵活的参数传递机制,可以传递简单类型、多个参数、对象、集合等

简单类型参数传递

如果方法只是接收一个简单类型(如int,String),可以用sql语句中#{} 传递参数,如以下

public interface StuMapper {
StuEntity getStuById(int id);
}
<select id="getStuById" resultType="StuEntity">    
SELECT * FROM students WHERE id = #{id}
</select>

多个参数传递

当参数有多个时,MyBatis不支持直接用多个参数名字,而是使用@Parm注释来为每一个参数命名

public interface StuMapper {
StuEntity getStuByIdAndName(@Param("id") int id, @Param("name") String name);
}
<select id="getStuByIdAndName" resultType="StuEntity">
SELECT * FROM students WHERE id = #{id} AND name = #{name}
</select>

解释

  • @Param("id"):为方法参数 id 赋予名称 id,同样为 name 参数赋予别名 name,这样可以在 SQL 中使用 #{id}#{name} 引用这些参数。

使用Map来传递参数

MyBatis 支持使用 Map 对象传递参数,Map 中的键可以在 SQL 中通过 #{} 来引用。

public interface StuMapper {
StuEntity getStuByMap(Map<String, Object> params);
}
<select id="getStuByMap" resultType="StuEntity">
SELECT * FROM students WHERE id = #{id} AND name = #{name}
</select>

如何调用?以下方法

Map<String, Object> params = new HashMap<>();
params.put("id", 1);
params.put("name", "John");
StuEntity student = stuMapper.getStuByMap(params);

解释

  • MyBatis 会将 Map 中的键值对映射到 SQL 中的 #{} 占位符,例如 #{id}#{name} 对应 Map 中的 idname