互联网轻量级框架整合之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 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对象,代码如下

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对象
    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、其三控制数据库事务

    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

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两部分,首先定义一个映射器接口

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的配置

    <mappers>
        <mapper resource="mapper/t_role.xml"/>
    </mappers>

其作用是引入一个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是用动态代理技术,来处理了相关的映射逻辑

用注解实现映射器
/**
 * 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类似

    <mappers>
        <mapper resource="mapper/t_role.xml"/>
        <mapper class="com.ssm.mapper.RoleMapperII"/>
    </mappers>

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

用SQLSession发送SQL

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

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,代码如下

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代表一个业务处理,一旦处理完相关业务,就应该抛弃它

代码示例

在这里插入图片描述

<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>

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);

}

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 + '\'' +
                '}';
    }
}

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 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>
# 此段为配置文件内容,而非函数或类代码,故按行进行注释解释

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

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

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

# 指定数据库的密码
password=Ms123!@#

# 配置文件的根记录器设置
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 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>

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();
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/533263.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用图和侧信息的核概率矩阵

文章信息 本周阅读的论文是一篇2012年发表在《Proceedings of the 2012 SIAM International Conference on Data Mining》上关于概率矩阵分解的文章&#xff0c;题目为《Kernelized Probabilistic Matrix Factorization Exploiting Graphs and Side Information》。 摘要 我们提…

【STM32篇】DRV8425驱动步进电机

【STM32篇】4988驱动步进电机_hr4988-CSDN博客 在上篇文章中使用了HR4988实现了步进电机的驱动&#xff0c;在实际运用过程&#xff0c;HR4988或者A4988驱动步进电机会存在电机噪音太大的现象。本次将向各位友友介绍一个驱动简单且非常静音的一款步进电机驱动IC。 1.DRV8425简介…

头歌-机器学习实验 第8次实验 决策树

第1关&#xff1a;什么是决策树 任务描述 本关任务&#xff1a;根据本节课所学知识完成本关所设置的选择题。 相关知识 为了完成本关任务&#xff0c;你需要掌握决策树的相关基础知识。 引例 在炎热的夏天&#xff0c;没有什么比冰镇后的西瓜更能令人感到心旷神怡的了。现…

Fast-Planner(五)详解TopologyPRM

本文上接Fast-Planner第一篇文章的内容&#xff0c;本文主要详解这一系列的第二篇Robust Real-time UAV Replanning Using Guided Gradient-based Optimization and Topological Paths中的TopologyPRM即其代码。如有问题&#xff0c;欢迎各位大佬评论指出&#xff0c;带着我一起…

C语言面试题之返回倒数第 k 个节点

返回倒数第 k 个节点 实例要求 1、实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点&#xff1b;2、返回该节点的值&#xff1b; 示例&#xff1a;输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说明&#xff1a;给定的 k 保证是有效的。实…

Unity 获取RenderTexture像素颜色值

拿来吧你~ &#x1f9aa;功能介绍&#x1f32d;Demo &#x1f9aa;功能介绍 &#x1f4a1;不通过Texture2D 而是通过ComputerShader 提取到RenderTexture的像素值&#xff0c;效率有提升哦&#xff01; &#x1f4a1;通过扩展方法调用&#xff0c;方便快捷&#xff1a;xxxRT.G…

无人机低空数字摄影测量系统

一、 系统概述 系统完全基于IDL设计实现&#xff0c;包括界面布局到人机交互再到底层核心函数功能。整体设计框架基于数字摄影测量的专业处理流程&#xff0c;实现了数据输入、数据预处理、影像信息检测、空间定向、地形三维建模、专题信息提取、成果输出与更新等功能。同时为…

【linux】yum 和 vim

yum 和 vim 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 查看软件包1.3 如何安装软件1.4 如何卸载软件1.5 关于 rzsz 2. Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 vim命令模式命令集2.4 vim底行模式命令集2.5 vim操作总结补充&#xff1a;vim下批量化注释…

Spring AI 应用 - 智能记者

参考实现&#xff1a; https://github.com/mshumer/ai-journalist 上面是通过 Claude 配合 SERP 搜索 API&#xff0c;使用 Python 语言实现的&#xff0c;本文通过 GitHub Copilot 辅助改为了基于 Spring AI 的 Java 版本&#xff0c;本文使用的 OpenAI。 AIJournalist 实现…

绝地求生:经典杜卡迪与传奇杜卡迪的区别以及亮点

大家好&#xff0c;这里是闲游盒. 杜卡迪联名已正式加入PUBG&#xff0c;本次联名分为两个版本分别为&#xff1a;经典杜卡迪以及传奇杜卡迪 那接下来&#xff0c;就给大家展示一下经典杜卡迪&#xff08;红&#xff09;以及传奇版本杜卡迪&#xff08;暮光粉&#xff09;的区…

Acwing.4009 收集卡牌(期望dp)

题目 小林在玩一个抽卡游戏&#xff0c;其中有 n种不同的卡牌&#xff0c;编号为 1到 n。 每一次抽卡&#xff0c;她获得第 i种卡牌的概率为 pi。 如果这张卡牌之前已经获得过了&#xff0c;就会转化为一枚硬币。 可以用 k枚硬币交换一张没有获得过的卡。 小林会一直抽卡&…

2024大模型落地应用案例集(免费下载)

【1】扫码关注本公众号 【2】私信发送 2024大模型落地应用案例集 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。

初识C++之内联函数 auto关键字

初识C之内联函数 auto关键字 文章目录 初识C之内联函数 auto关键字一、 内联函数1.1 定义1.2 应用1.3 特性 二、auto关键字2.1 简介2.2 auto的详细使用2.3 范围for&#xff08;C&#xff09;2.4 注意事项 一、 内联函数 1.1 定义 以inline修饰的函数叫做内联函数&#xff0c;…

UDP实现Mini版在线聊天室

实现原理 只有当客户端先对服务器发送online消息的时候&#xff0c;服务器才会把客户端加入到在线列表。当在线列表的用户发消息的时候&#xff0c;服务器会把消息广播给在线列表中的所有用户。而当用户输入offline时&#xff0c;表明自己要下线了&#xff0c;此时服务器把该用…

跨域问题一文解决

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Vue ⛺️稳中求进&#xff0c;晒太阳 一、为什么会出现跨域的问题&#xff1f; 是浏览器的同源策略&#xff0c;跨域也是因为浏览器这个机制引起的&#xff0c;这个机制的存在还是在于安全…

【MATLAB源码-第6期】基于matlab的QPSK的误码率BER和误符号率SER仿真。

1、算法描述 QPSK&#xff0c;有时也称作四位元PSK、四相位PSK、4-PSK&#xff0c;在坐标图上看是圆上四个对称的点。通过四个相位&#xff0c;QPSK可以编码2位元符号。图中采用格雷码来达到最小位元错误率&#xff08;BER&#xff09; — 是BPSK的两倍. 这意味著可以在BPSK系统…

利用DataX工具,实现MySQL与OceanBase的数据同步实践

数据迁移是经常遇到的需求&#xff0c;市面上为此提供了众多同步工具。这里将为大家简要介绍DataX的使用。DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;它作为离线数据同步的工具/平台&#xff0c;在阿里巴巴集团内部被广泛应用。DataX 能够实现多种异构数据源之间…

图书馆自习室|基于SSM的图书馆自习室座位预约小程序设计与实现(源码+数据库+文档)

图书馆自习室目录 基于SSM的图书馆自习室座位预约小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1、小程序端&#xff1a; 2、后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a…

[opencv]VideoWriter写出fourcc格式

fourcc支持的格式 fourcc全名Four-Character Codes&#xff0c;四字符代码&#xff0c;该编码由四个字符组成 cv2.VideoWriter_fourcc(O,O,O,O) cv2.VideoWriter_fourcc(*OOOO) 通常写法有上述两种形式&#xff0c;O代表一个字符&#xff0c;通常有 支持avi格式的有&#…

麒麟KOS删除鼠标右键新建菜单里不需要的选项

原文链接&#xff1a;麒麟KOS删除鼠标右键新建菜单里不需要的选项 Hello&#xff0c;大家好啊&#xff01;在日常使用麒麟KOS操作系统时&#xff0c;我们可能会发现鼠标右键新建菜单里包含了一些不常用或者不需要的选项。这不仅影响我们的使用效率&#xff0c;也让菜单显得杂乱…