互联网轻量级框架整合之MyBatis核心组件

在看本篇内容之前,最好先理解一下Hibernate和MyBatis的本质区别,这篇Hibernate和MyBatis使用对比实例做了实际的代码级对比,而MyBatis作为更适合互联网产品的持久层首选必定有必然的原因

MyBatis核心组件

MyBatis能够成为数据持久层首选框,关键还是在于ORM(Object-Relational Mapping)的特性上:

  • 不屏蔽SQL,这意味着可以更精确的定位SQL语句,可以对其进行优化和改造,非常有利于互联网的高可用和高并发属性,符合互联网产品需要的高性能特点
  • 提供强大、灵活的映射机制,提供动态SQL的特性,允许根据不同条件组装SQL,这个特点远比其他工具或者Java编码的可读性和可维护性更好,满足各种应用系统需求的同时满足了需求经常变化的互联网产品要求
  • 提供Mapper接口编程,只需要一个接口和一个XML就能够构建映射器,进一步简化开发工作,从而可以更聚焦于业务逻辑

MyBatis的核心组件分为4个部分:

  • SqlSessionFactoryBuilder(构造器):根据配置或者代码生成SqlSessionFactory,采用分布构建的Builder模式
  • SqlSessionFactory(工厂接口):生成SqlSession,使用工厂模式
  • SqlSession(会话):既可以发送SQL执行返回结果,也可以获取Mapper的接口,在现有的模式中,通常会使用MyBatis提供的SQL Mapper接口编程技术,以提高代码的可读性和可维护性
  • SqlMapper(映射器):由Java接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则,由他来发送SQL去执行,并返回结果

SQLSessionFactory

使用MyBatis需要使用配置或者代码构建SQLSessionFactory(工厂接口)对象,MyBatis提供了构造器SQLSessionFactoryBuilder,而这就是建造者模式,一步构建SQLSessionFactoryBuilder会比较复杂,为了简化这个对象的构建过程,MyBatis提供了一个配置类org.apache.ibatis.session.Configuration作为引导,实际的分步构建过程是在Configuration类里完成的

这里边会有很多其他内容,例如比较复杂的插件等等

构建SQLSessionFactory对象还可以采用读取配置好的XML文件的形式(通过Java代码的形式需要修改则会相对复杂),无论是配置了XML文件或者提供了代码后,MyBatis会读取配置文件,通过Configuration类对象构建整个MyBatis的上下文

  • SQLSessionFactory是一个接口,在MyBatis中存在SQLSessionManager和DefaultSQLSessionFactory两个实现类,前者多用于多线程环境中,通常情况下是使用后者来实现的
  • 每个基于MyBatis的应用都是以一个SQLSessionFactory的实例为中心的,而SQLSessionFactory唯一的作用就是生产MyBatis的核心接口对象SQLSession,因此它的责任是唯一的,在这样的情况下,我们应该只构建一个SQLSessionFactory对象,显然就是一种单例模式
使用XML配置文件构建SQLSessionFactory对象
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTDConfig3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">


<configuration>
    <!--typeAliases必须放在properties后面,否则会报错 -->
    <properties resource="config.properties"/>
    <typeAliases>
        <typeAlias alias="role" type="com.ssm.pojo.Role" />
    </typeAliases>
    <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/ssm1?useUnicode=true;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="Ms123!@#"/>
                -->
                <property name="driver" value="${className}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>

            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/t_role.xml"/>
    </mappers>
</configuration>

这段XML代码是MyBatis配置文件的示例。

  • typeAliases 定义了类型别名,这里将 com.ssm.Role 类型别名为 Role,定义别名后在MyBatis上下文中就可以使用别名代替全限定名
  • properties 用于加载外部属性文件 config.properties。
  • environments 定义了数据库连接的环境配置,默认使用 development 环境。
  • transactionManager 设置事务管理器类型为 JDBC。
  • dataSource 设置数据源类型为 POOLED,表示采用MyBatis内部提供的连接池方式。
  • mappers 定义了映射文件的位置,这里指定了 mapper/t_role.xml。

有了这个配置就可以用一段代码来构建SQLSessionFactory对象,代码如下

java 复制代码
package com.ssm;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 创建SqlSessionFactory类,用于单例模式下创建MyBatis的SqlSessionFactory对象。
 * 这个类不包含任何参数构造函数,通过静态方法创建SqlSessionFactory实例。
 */
public class CreateSqlSessionFactory
{
    /**
     * 静态方法,创建并返回一个SqlSessionFactory实例。
     * 这个方法通过读取配置文件"mybatis-config.xml"来构建SqlSessionFactory。
     *
     * @return SqlSessionFactory MyBatis的SQL会话工厂对象。
     */
    private static SqlSessionFactory createSqlSessionFactory() {
        SqlSessionFactory sqlSessionFactory = null; // 初始化SqlSessionFactory为null
        String cfgFile = "mybatis-config.xml"; // MyBatis配置文件的路径
        InputStream inputStream = null; // 初始化输入流为null
        try {
            // 尝试从资源中加载配置文件的输入流
            inputStream = Resources.getResourceAsStream(cfgFile);
            // 使用输入流构建SqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }catch (IOException e){
            e.printStackTrace(); // 捕获并打印IO异常
        }
        return sqlSessionFactory; // 返回构建的SqlSessionFactory实例
    }
}
使用代码构建SQLSessionFactory对象
java 复制代码
    private static SqlSessionFactory createSqlSessionFactoryII(){
        // 初始化数据源
        PooledDataSource dataSource = new PooledDataSource();
        dataSource.setDriver("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc://localhost:3306/ssm");
        dataSource.setDefaultAutoCommit(false);

        // 设置事务工厂和环境配置
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource);

        // 配置MyBatis
        Configuration configuration = new Configuration(environment);
        // 注册别名,使得在映射文件中可以使用别名代替全类名
        configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
        // 添加映射器,用于映射SQL语句到Java方法
        configuration.addMapper(RoleMapper.class);

        // 构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        return sqlSessionFactory;
    }

这种方式比较复杂,如果修改代码还需要重新编译等动作,实际上完成了跟XML形式一样的事情,但如果配置文件中配置的是加密后的数据库用户名和密码,需要在构建SQLSessionFactory对象之前进行解密,可以采用这种形式

SQLSession

在MyBatis中,SQLSession也是核心接口之一,在MyBatis中有两个实现类,DefaultSqlSession和SqlSessionManager,DefaultSqlSession是单线程环境下使用,而SqlSessionManager在多线程环境下使用;SqlSession的作用类似于一个JDBC中的Connection对象,代表着一个链接资源的启用,如要作用有3个其一获取Mapper接口、其二给数据库发送SQL、其三控制数据库事务

java 复制代码
    public void executeSqlSessionOperation() {
        // 假设这里是你需要执行的具体逻辑操作
        // 注意:由于原代码没有具体的业务逻辑,以下代码将展示如何在try-with-resources中结构化处理SqlSession
        try (SqlSession sqlSession = createSqlSessionFactory().openSession()) {
            // 执行SqlSession中的操作,例如:sqlSession.insert(...); sqlSession.update(...); 等

            // 提交事务
            sqlSession.commit();
        } catch (Exception e) {
            // 对异常进行处理,这里可以根据不同的异常类型进行更细致的捕获和处理
            // 例如,对于数据库连接异常、SQL执行异常等,可以进行不同的日志记录或者错误处理
            e.printStackTrace();
            // 回滚事务,由于在try块中无法直接访问到SqlSession,所以这里假设异常发生时需要回滚事务
            // 注意:实际中可能需要更复杂的逻辑来确保事务的正确回滚
            sqlSession.rollback();
        }finally {
            if(sqlSession != null){
                sqlSession.close();
            }
        }
    }

映射器

映射器是MyBatis中相对复杂的组件,它由一个接口和对应的XML文件(或注解)组成,它可以配置一下内容

  • 描述映射规则
  • 提供SQL语句,并配置SQL参数类型、返回类型、缓存刷新等信息
  • 配置缓存
  • 提供动态SQL

首先定义一个POJO

java 复制代码
package com.ssm.pojo;
/**
 * 角色类,用于表示系统中的角色信息
 */
public class Role {
    private Integer id; // 角色ID
    private String roleName; // 角色名称
    private String note; // 角色备注信息

    /**
     * 获取角色ID
     * @return 角色的ID值
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置角色ID
     * @param id 角色的新ID值
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取角色名称
     * @return 角色的名称
     */
    public String getRoleName() {
        return roleName;
    }

    /**
     * 设置角色名称
     * @param roleName 角色的新名称
     */
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    /**
     * 获取角色备注信息
     * @return 角色的备注信息
     */
    public String getNote() {
        return note;
    }

    /**
     * 设置角色备注信息
     * @param note 角色的新备注信息
     */
    public void setNote(String note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", note='" + note + '\'' +
                '}';
    }
}

在Java中,使用private关键字来修饰类的成员变量(如上述Role类中的id, roleName, note)的主要原因如下:

  • 封装性(Encapsulation):这是面向对象编程(OOP)的基本原则之一。将数据成员声明为private意味着它们不能直接被外部访问或修改。这样可以隐藏对象内部实现细节,对外提供一个清晰、稳定的接口。通过只暴露必要的公共方法(如getId(), setId(), getRoleName(), setRoleName()等),控制了对这些私有成员变量的访问和操作,确保了数据的安全性和一致性。
  • 数据保护:private修饰的变量仅能在该类的内部方法中访问和修改。这样可以防止外部代码(如其他类或同一个类的不同实例)意外地更改或错误地使用这些数据,从而避免潜在的数据损坏或逻辑错误。例如,对于id这样的标识符,通常希望其一旦初始化后就不允许随意变更,通过将其设为private并提供无set方法,就可以有效防止外部代码误改。
  • 实现内聚性:将变量设为private并提供对应的getter和setter方法(即访问器和修改器),可以集中处理与这些变量相关的业务逻辑。例如,在setNote()方法中,可以添加额外的校验逻辑(如检查输入字符串是否合法)、触发相关事件(如更新日志记录)或执行其他与设置note属性相关的操作。这样使得类的职责更加明确,提高了代码的内聚性。
  • 灵活性与可维护性:将来如果需要更改Role类内部数据的存储方式(如从使用String存储roleName改为使用枚举类型),或者添加新的约束条件(如限制note的最大长度),由于外部代码仅依赖于提供的公共方法,而不直接操作私有变量,因此无需修改大量外部代码,极大地提高了代码的灵活性和可维护性。

综上所述,将POJO(Plain Old Java Object,简单Java对象)中的变量定义为private,是为了实现面向对象编程中的封装性原则,保护数据安全,增强代码内聚性,以及提高代码的灵活性和可维护性。

MyBatis的映射器的作用主要是在POJO和SQL之间建立映射关系

用XML实现映射器

用XML定义映射器分为接口和XML两部分,首先定义一个映射器接口

java 复制代码
package com.ssm.Dao;

import com.ssm.pojo.Role;

import java.util.List;
/**
 * 角色数据访问接口
 */
public interface RoleDao {

    /**
     * 根据角色ID获取角色信息
     * @param id 角色的唯一标识符
     * @return 返回对应ID的角色对象,如果不存在则返回null
     */
    public Role getRoleById(Integer id);

    /**
     * 根据角色名称获取角色信息
     * @param name 角色的名称
     * @return 返回对应名称的角色对象,如果不存在则返回null
     */
    public Role getRoleByName(String name);

    /**
     * 新增角色信息
     * @param role 需要新增的角色对象
     */
    public void insertRole(Role role);

    /**
     * 更新角色信息
     * @param role 需要更新的角色对象
     */
    public void updateRole(Role role);

    /**
     * 根据角色ID删除角色信息
     * @param id 需要删除的角色的唯一标识符
     */
    public void deleteRole(Integer id);

    /**
     * 获取所有角色信息
     * @return 返回所有角色的列表
     */
    public List<Role> findRoles(String roleName);
}

在mybatis-config.xml文件里有一行mapper的配置

xml 复制代码
    <mappers>
        <mapper resource="mapper/t_role.xml"/>
    </mappers>

其作用是引入一个XML配置文件,通过XML方式构建映射器

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的命名空间 -->
<mapper namespace="com.ssm.mapper.RoleMapper">
    <!-- 定义结果映射,用于将数据库中的数据映射成Role对象 -->
    <resultMap id="roleMap" type="com.ssm.pojo.Role">
        <id property="id" column="id"/> <!-- 主键映射 -->
        <result property="roleName" column="roleName"/> <!-- 角色名映射 -->
        <result property="note" column="note"/> <!-- 备注映射 -->
    </resultMap>

    <!-- 插入一个新的Role记录 -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role">
        INSERT INTO t_role(roleName, note) VALUES (#{roleName}, #{note})
    </insert>

    <!-- 根据ID查询Role信息,返回Role对象 -->
    <select id="getRoleById" parameterType="int" resultMap="roleMap">
        SELECT * FROM t_role WHERE id = #{id}
    </select>

    <!-- 更新Role的信息 -->
    <update id="updateRole" parameterType="com.ssm.pojo.Role">
        UPDATE t_role SET roleName = #{roleName}, note = #{note} WHERE id = #{id}
    </update>

    <!-- 根据ID删除一个Role记录 -->
    <delete id="deleteRole" parameterType="int">
        DELETE FROM t_role WHERE id = #{id}
    </delete>
</mapper>

有了接口和XML映射文件,就完成了一个映射器的定义,MyBatis在默认情况下提供自动映射功能,只要SQL返回的列名能和POJO对应起来即可;很显然定义映射器只用到了接口,而没有实现类,接口是不能直接运行的,MyBatis是用动态代理技术,来处理了相关的映射逻辑

用注解实现映射器
java 复制代码
/**
 * RoleMapperII接口,用于通过ID获取角色信息
 */
package com.ssm.mapper;

import com.ssm.pojo.Role;
import org.apache.ibatis.annotations.Select;

public interface RoleMapperII {
    /**
     * 根据角色ID获取角色详细信息
     * @param id 角色的唯一标识符
     * @return 返回对应ID的角色对象
     */
    @Select("select * from role where id = #{id}")
    public Role getRoleById(Integer id);
}

这完全等同于通过XML方式构建映射器,如果两个方式同时定义时,XML方式将覆盖掉注解方式,如果使用注解的方式则需要在MyBatis基础配置文件中添加mapper接口,和配置XML类似

xml 复制代码
    <mappers>
        <mapper resource="mapper/t_role.xml"/>
        <mapper class="com.ssm.mapper.RoleMapperII"/>
    </mappers>

并且在代码中使用Configuration对象注册这个映射器接口configuration.addMapper(RoleMapperII.class);

用SQLSession发送SQL

有了映射器就可以通过SQLSession发送SQL了,如下代码所示

java 复制代码
Role role=(Role)sqlSession;
selectOne("com.ssm.mapper.RoleMapper.getRoleByID", 1);

selectOne方法表示使用查询并且只返回一个对象,参数一看就很清楚来自哪里,如此字符串对象由一个命名空间加上SQL ID组合而成,它完全定位到了一条SQL,这条语句也可以简化为Role role=(Role)sqlSession.selectOne("getRole",1);这是MyBatis的前身IBATIS留下的方式

用Mapper接口发送SQL

SQLSession还可以获取Mapper接口,用Mapper接口发送SQL,代码如下

java 复制代码
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1);

因为XML文件或者接口注解定义的SQL都可以通过"类的全限定名+方法名"查找,因此MyBatis会启用对应接口执行SQL并返回结果

  • 用注解的形式实现映射器,如果遇到很复杂的SQL,那代码的可读性实在太差了,更严重的如果遇到动态SQL,那就会更加复杂,Java代码和SQL混杂一起,非常难读且难以维护
  • 发送SQL如果使用SQLSession直接发送,那只会在运行过程中才能知道是否会产生错误,而如果使用Mapper的方式发送SQL,IDE会提示错误和校验,并且使用SQLSession发送SQL也会给代码带来一定的复杂度,虽然不至于很难读,但也不那么美观

生命周期

生命周期是MyBatis组件的重要问题,尤其在多线程环境中,控制不好会造成严重的多线程并发或者系统资源浪费问题

  • SQLSessionFactoryBuilder:的作用是构建SQLSessionFactory,构建成功后,SQLSessionFactoryBuilder就失去了作用,所以他只能存在于构建SQLSessionFactory的方法中,而不能长期存在
  • SQLSessionFactory:可以认为它是一个数据库的连接池,其作用是构建SQLSession接口对象,而MyBatis的本质是对数据库的操作,因此SQLSessionFactory的生命周期存在于整个MyBatis的应用中,一旦构建了SQLSessionFactory,就要长期保存,直到不再使用MyBatis应用;而由于SQLSessionFactory相当于一个对数据库的连接池,它占据着数据库的链接资源,如果构建多个SQLSessionFactory,就存在多个连接池,这样不利于对数据库资源的合理使用,容易导致数据库连接多不好管理,数据库资源快速耗光而宕机,因此通常希望只有一个SQLSessionFactory作为单例,然后让他在应用中被共享
  • SQLSession:如果SQLSessionFactory相当于数据库连接池,那么SQLSession就相当于一个数据库连接(Connection对象),用户可以在一个事务里执行多条SQL,然后通过它的commit、rollback等方法进行提交或者回滚事务;SQLSession应该只存在当前业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给SQLSessionFactory,否则数据库资源很快会耗光,导致系统瘫痪,所以需要用try...catch...finally结构体来保证其能够正确关闭并释放诗句哭链接资源
  • Mapper:Mapper是一个接口,由SQLSession构建,所以它的生命周期至多和SQLSession保持一致,会随着SQLSession的关闭而消失,这是合理的,Mapper代表一个业务处理,一旦处理完相关业务,就应该抛弃它

代码示例

xml 复制代码
<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.ssm</groupId>
  <artifactId>ChapterMybatis</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>ChapterMybatis</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.9</version>
    </dependency>
    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.24.1-GA</version>
    </dependency>
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.2.10</version>
    </dependency>
    <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm</artifactId>
      <version>7.0</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.26</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.26</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.11.2</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
  </dependencies>
</project>
java 复制代码
package com.ssm.Dao;

import com.ssm.pojo.Role;

import java.util.List;
/**
 * 角色数据访问接口
 */
public interface RoleDao {

    /**
     * 根据角色ID获取角色信息
     * @param id 角色的唯一标识符
     * @return 返回对应ID的角色对象,如果不存在则返回null
     */
    public Role getRoleById(Integer id);

    /**
     * 根据角色名称获取角色信息
     * @param name 角色的名称
     * @return 返回对应名称的角色对象,如果不存在则返回null
     */
    public Role getRoleByName(String name);

    /**
     * 新增角色信息
     * @param role 需要新增的角色对象
     */
    public void insertRole(Role role);

    /**
     * 更新角色信息
     * @param role 需要更新的角色对象
     */
    public void updateRole(Role role);

    /**
     * 根据角色ID删除角色信息
     * @param id 需要删除的角色的唯一标识符
     */
    public void deleteRole(Integer id);

    /**
     * 获取所有角色信息
     * @return 返回所有角色的列表
     */
    public List<Role> findRoles(String roleName);

}
java 复制代码
package com.ssm.pojo;
/**
 * 角色类,用于表示系统中的角色信息
 */
public class Role {
    private Integer id; // 角色ID
    private String roleName; // 角色名称
    private String note; // 角色备注信息

    /**
     * 获取角色ID
     * @return 角色的ID值
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置角色ID
     * @param id 角色的新ID值
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取角色名称
     * @return 角色的名称
     */
    public String getRoleName() {
        return roleName;
    }

    /**
     * 设置角色名称
     * @param roleName 角色的新名称
     */
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    /**
     * 获取角色备注信息
     * @return 角色的备注信息
     */
    public String getNote() {
        return note;
    }

    /**
     * 设置角色备注信息
     * @param note 角色的新备注信息
     */
    public void setNote(String note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", note='" + note + '\'' +
                '}';
    }
}
java 复制代码
package com.ssm.Utils;

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 java.io.IOException;
import java.io.InputStream;

/**
 * 创建SqlSessionFactory类,用于单例模式下创建MyBatis的SqlSessionFactory对象。
 * 这个类不包含任何参数构造函数,通过静态方法创建SqlSessionFactory实例。
 */
public class SqlSessionFactoryUtils {

    //同步锁,用于确保线程安全地创建单例SqlSessionFactory对象
    private final static Class<SqlSessionFactoryUtils> LOCK = SqlSessionFactoryUtils.class;

    //SqlSessionFactory单例对象
    private static SqlSessionFactory sqlSessionFactory = null;

    //私有构造函数,防止外部实例化本类
    private SqlSessionFactoryUtils(){
        System.out.println("SqlSessionFactoryUtils类被实例化了");
    }
    /**
     * 静态方法,创建并返回一个SqlSessionFactory实例。
     * 这个方法通过读取配置文件"mybatis-config.xml"来构建SqlSessionFactory。
     * 方法是线程安全的,确保了SqlSessionFactory的单例特性。
     */
    public static void getSqlSessionFactory() {
        synchronized (LOCK) { //使用同步代码块,保证线程安全
            if (sqlSessionFactory != null) {
                //如果SqlSessionFactory已经存在,则直接返回,避免重复创建
                return;
            }
            //读取配置文件
            String configFile = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(configFile);
                //基于配置文件构建SqlSessionFactory
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 打开一个SqlSession会话。
     * 如果SqlSessionFactory尚未创建,则先创建之。
     *
     * @return 返回一个SqlSession实例。
     */
    public static SqlSession openSqlSession(){
        //确保SqlSessionFactory已经创建
        if(sqlSessionFactory ==null){
            getSqlSessionFactory();
        }
        //返回一个新的SqlSession实例
        return sqlSessionFactory.openSession();
    }
}
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTDConfig3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">


<configuration>
    <!--typeAliases必须放在properties后面,否则会报错 -->
    <properties resource="config.properties"/>
    <typeAliases>
        <typeAlias alias="Role" type="com.ssm.pojo.Role"/>
    </typeAliases>

    <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/ssm1?useUnicode=true;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="Ms123!@#"/>
                -->
                <property name="driver" value="${className}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>

            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/RoleMapper.xml"/>
        <mapper class="com.ssm.Dao.RoleDaoII"/>
    </mappers>
</configuration>
properties 复制代码
# 此段为配置文件内容,而非函数或类代码,故按行进行注释解释

# 指定MySQL连接的驱动类
className=com.mysql.cj.jdbc.Driver

# 设置数据库连接的URL,包括数据库地址、端口和数据库名称等信息
url=jdbc:mysql://localhost:3306/ssm1?useUnicode=true&characterEncoding=utf8

# 指定数据库的用户名
username=root

# 指定数据库的密码
password=Ms123!@#
properties 复制代码
# 配置文件的根记录器设置
log4j.rootLogger=DEBUG, stdout

# 设置org.mybatis包的记录级别为DEBUG
log4j.logger.org.mybatis=DEBUG

# 配置标准输出流作为日志的输出目标
log4j.appender.stdout=org.apache.log4j.ConsoleAppender

# 配置标准输出流的布局模式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
# 上述模式为日志输出的格式,%5p表示日志级别,%d表示日期,%C表示类别,%m表示日志消息,%n表示换行
# 下面的注释掉的配置是一种替代的日期格式和日志输出格式
# log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
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的命名空间 -->
<mapper namespace="com.ssm.Dao.RoleDao">
    <!-- 定义结果映射,用于将数据库中的数据映射成Role对象 -->
    <resultMap id="roleMap" type="com.ssm.pojo.Role">
        <id property="id" column="id"/> <!-- 主键映射 -->
        <result property="roleName" column="roleName"/> <!-- 角色名映射 -->
        <result property="note" column="note"/> <!-- 备注映射 -->
    </resultMap>

    <!-- 插入一个新的Role记录 -->
    <insert id="insertRole" parameterType="com.ssm.pojo.Role">
        INSERT INTO t_role(roleName, note) VALUES (#{roleName}, #{note})
    </insert>

    <!-- 根据ID查询Role信息,返回Role对象 -->
    <select id="getRoleById" parameterType="int" resultMap="roleMap">
        SELECT id, roleName, note FROM t_role WHERE id = #{id}
    </select>

    <select id="findRoles" parameterType="string" resultMap="roleMap">
        select id, roleName, note from t_role where roleName like concat ('%',#{roleName},'%')
    </select>

    <!-- 更新Role的信息 -->
    <update id="updateRole" parameterType="com.ssm.pojo.Role">
        UPDATE t_role SET roleName = #{roleName}, note = #{note} WHERE id = #{id}
    </update>

    <!-- 根据ID删除一个Role记录 -->
    <delete id="deleteRole" parameterType="int">
        DELETE FROM t_role WHERE id = #{id}
    </delete>
</mapper>
java 复制代码
package com.ssm;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import com.ssm.Dao.RoleDao;
import com.ssm.pojo.Role;
import com.ssm.Utils.SqlSessionFactoryUtils;

import java.util.List;

/**
 * 主程序类,用于演示通过MyBatis进行数据库操作。
 */
public class Main {
    public static void main(String[] args) {
        // 获取日志记录器
        Logger log = Logger.getLogger(Main.class);
        SqlSession sqlSession = null;
        try{
            // 获取SqlSession实例
            sqlSession = SqlSessionFactoryUtils.openSqlSession();
            // 通过SqlSession获取RoleDao接口的实现
            RoleDao roleDao = sqlSession.getMapper(RoleDao.class);

            // 调用方法查询角色列表
            List<Role> role = roleDao.findRoles("zhang");
            // 记录查询结果
            log.info(role.toString());

            // 通过ID查询单个角色
            Role role1 = roleDao.getRoleById(2);
            // 记录查询结果
            log.info(role1.toString());

            // 插入角色到数据库
            roleDao.insertRole(role1);
        } finally {
            // 确保SqlSession在操作完成后关闭
            if (sqlSession != null){
                sqlSession.close();
            }
        }
    }
}
相关推荐
cmdch20176 小时前
Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题
数据库·sql·mybatis
秋恬意9 小时前
什么是MyBatis
mybatis
CodeChampion9 小时前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
ZWZhangYu1 天前
【MyBatis源码分析】使用 Java 动态代理,实现一个简单的插件机制
java·python·mybatis
程序员大金1 天前
基于SSM+Vue的个性化旅游推荐系统
前端·vue.js·mysql·java-ee·tomcat·mybatis·旅游
奔跑草-2 天前
【服务器】MyBatis是如何在java中使用并进行分页的?
java·服务器·mybatis
秋恬意2 天前
接口绑定有几种实现方式
mybatis
谢家小布柔2 天前
MyBatis入门的详细应用实例
mybatis
雅俗共赏zyyyyyy2 天前
Mybatis分页插件的使用问题记录
mybatis
陈大爷(有低保)2 天前
mybatisPlus使用步骤详解
java·后端·mybatis