【Mybatis】知识点总结

cangloe 2024-09-12 12:05:06 阅读 65

MyBatis 是一个持久化框架(persistence framework),它简化了对数据库的访问,同时又保留了对 SQL 的控制。它在数据持久化的过程中,避免了大量的 JDBC 代码以及手动设置参数和获取结果的繁琐步骤。相比于 Hibernate 等全自动 ORM 框架,MyBatis 更轻量级且灵活,特别适用于需要手写复杂 SQL 的场景。接下来,我们将详细介绍 MyBatis 的各个方面。

目录

MyBatis 简介MyBatis 的基本概念MyBatis 配置文件Mapper 文件Mapper 接口动态 SQLMyBatis 与 Spring 整合MyBatis 进阶特性MyBatis 与其他持久化框架的对比实战案例


1. MyBatis 简介

1.1 MyBatis 是什么?

MyBatis 是 Apache Software Foundation 旗下的一个项目,用于在 Java 应用中处理持久化层(Persistence Layer)。它通过 XML 或注解的方式将实体类和 SQL 语句进行映射,可以帮助开发者快速进行 CRUD(Create、Read、Update、Delete)操作。

1.2 MyBatis 的特点

轻量级:MyBatis 不需要像 Hibernate 那样的全局配置,配置简单灵活。SQL 控制:MyBatis 允许开发者手写 SQL,直接与数据库交互,便于处理复杂的 SQL 查询。可插拔性:MyBatis 可以与其他框架(如 Spring)无缝集成。动态 SQL:支持动态 SQL 生成,以适应不同的查询条件。缓存支持:支持一级缓存和二级缓存,提高性能。


2. MyBatis 的基本概念

2.1 SqlSessionFactory

作用:负责创建 <code>SqlSession 实例。配置:通过 XML 配置文件或 Java 配置类进行构建。

String resource = "mybatis-config.xml";

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.2 SqlSession

作用:用于执行 SQL 操作,如查询、插入、更新和删除。生命周期:应为每次数据库操作创建一个新的 SqlSession,并在操作完成后关闭。

try (SqlSession session = sqlSessionFactory.openSession()) {

UserMapper mapper = session.getMapper(UserMapper.class);

User user = mapper.selectUserById(1);

// 其他操作

session.commit(); // 提交事务

}

2.3 Mapper 接口

作用:定义数据库操作的接口,MyBatis 自动生成实现类。配置方式:可以通过 XML 文件或注解进行配置。

public interface UserMapper {

User selectUserById(int id);

void insertUser(User user);

List<User> selectAllUsers();

}

2.4 实体类(POJO)

作用:与数据库表结构相对应的 Java 类。要求:需要有与数据库字段相对应的属性,以及相应的 getter 和 setter 方法。

public class User {

private int id;

private String username;

private String password;

private String email;

// getter 和 setter 方法

}


3. MyBatis 配置文件

MyBatis 的配置文件是整个框架的核心,它定义了数据库连接、事务管理、Mapper 映射等。

作用:配置 MyBatis 的全局属性,如数据源、事务管理等。文件名:通常为 mybatis-config.xml

3.1 环境配置

配置多个环境(development、test、production):

<environments default="development">code>

<environment id="development">code>

<transactionManager type="JDBC"/>code>

<dataSource type="POOLED">code>

<property name="driver" value="com.mysql.cj.jdbc.Driver"/>code>

<property name="url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC"/>code>

<property name="username" value="root"/>code>

<property name="password" value="password"/>code>

</dataSource>

</environment>

<environment id="production">code>

<transactionManager type="JDBC"/>code>

<dataSource type="POOLED">code>

<property name="driver" value="com.mysql.cj.jdbc.Driver"/>code>

<property name="url" value="jdbc:mysql://production-db:3306/proddb?useSSL=false&serverTimezone=UTC"/>code>

<property name="username" value="prod_user"/>code>

<property name="password" value="prod_password"/>code>

</dataSource>

</environment>

</environments>

解析

default:指定默认环境。transactionManager

JDBC:使用 JDBC 提供的事务管理。MANAGED:应用服务器或 Spring 管理事务。 dataSource

UNPOOLED:每次请求都会创建新连接,适用于简单应用。POOLED:使用连接池提高性能,适用于生产环境。JNDI:使用应用服务器的数据源。

3.2 Mapper 配置

使用 XML 配置 Mapper

<mappers>

<mapper resource="com/example/mapper/UserMapper.xml"/>code>

</mappers>

使用注解配置 Mapper

<mappers>

<mapper class="com.example.mapper.UserMapper"/>code>

</mappers>

自动扫描 Mapper 包

<mappers>

<package name="com.example.mapper"/>code>

</mappers>

3.3 类型别名

类型别名用于简化 XML 中类的引用,可以为常用类指定简短别名。

<typeAliases>

<typeAlias type="com.example.entity.User" alias="User"/>code>

</typeAliases>

可以使用包扫描自动为包下的类生成别名:

<typeAliases>

<package name="com.example.entity"/>code>

</typeAliases>

3.4 插件配置

MyBatis 支持插件扩展,可以通过插件实现自定义功能,如日志、分页等。

<plugins>

<plugin interceptor="com.example.plugin.ExamplePlugin">code>

<property name="someProperty" value="100"/>code>

</plugin>

</plugins>

3.5 全局属性配置

<settings>

<setting name="cacheEnabled" value="true"/>code>

<setting name="lazyLoadingEnabled" value="true"/>code>

<setting name="multipleResultSetsEnabled" value="true"/>code>

<setting name="useGeneratedKeys" value="true"/>code>

<setting name="defaultExecutorType" value="SIMPLE"/>code>

<setting name="defaultStatementTimeout" value="25000"/>code>

</settings>

常用全局属性

cacheEnabled:启用或禁用二级缓存。lazyLoadingEnabled:启用或禁用延迟加载。useGeneratedKeys:允许 JDBC 支持生成主键。defaultExecutorType

SIMPLE:每次执行都会打开和关闭 Statement。REUSE:复用 Statement。BATCH:批量执行 Statement。 defaultStatementTimeout:设置超时时间(秒)。

3.6 自定义类型处理器

MyBatis 提供了默认类型处理器,也支持自定义处理器来处理特殊类型。

<typeHandlers>

<typeHandler javaType="java.util.Date" jdbcType="TIMESTAMP" handler="com.example.handler.DateTypeHandler"/>code>

</typeHandlers>

自定义类型处理器示例:

package com.example.handler;

import org.apache.ibatis.type.BaseTypeHandler;

import org.apache.ibatis.type.JdbcType;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Timestamp;

import java.util.Date;

public class DateTypeHandler extends BaseTypeHandler<Date> {

@Override

public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {

ps.setTimestamp(i, new Timestamp(parameter.getTime()));

}

@Override

public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {

Timestamp timestamp = rs.getTimestamp(columnName);

return timestamp != null ? new Date(timestamp.getTime()) : null;

}

@Override

public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

Timestamp timestamp = rs.getTimestamp(columnIndex);

return timestamp != null ? new Date(timestamp.getTime()) : null;

}

@Override

public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {

Timestamp timestamp = cs.getTimestamp(columnIndex);

return timestamp != null ? new Date(timestamp.getTime()) : null;

}

}

3.7 日志配置

MyBatis 支持多种日志框架,包括 SLF4J、Log4j、Commons Logging、JDK Logging。

使用 SLF4J

<settings>

<setting name="logImpl" value="SLF4J"/>code>

</settings>

使用 Log4j

<settings>

<setting name="logImpl" value="LOG4J"/>code>

</settings>

可以在log4j.properties中配置日志级别:

log4j.rootLogger=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n


4. Mapper 文件

Mapper 文件是 MyBatis 中的核心组件,它定义了 SQL 语句和实体类之间的映射关系。

4.1 Mapper XML 文件结构

<?xml version="1.0" encoding="UTF-8" ?>code>

<!DOCTYPE mapper

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.mapper.UserMapper">code>

<!-- ResultMap 定义 -->

<resultMap id="userResultMap" type="com.example.entity.User">code>

<id property="id" column="id"/>code>

<result property="username" column="username"/>code>

<result property="password" column="password"/>code>

<result property="email" column="email"/>code>

</resultMap>

<!-- SQL 片段 -->

<sql id="selectAllFields">code>

id, username, password, email

</sql>

<!-- 查询操作 -->

<select id="selectUserById" resultMap="userResultMap" parameterType="int">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

WHERE id = #{id}

</select>

<!-- 插入操作 -->

<insert id="insertUser" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">code>

INSERT INTO users (username, password, email)

VALUES (#{username}, #{password}, #{email})

</insert>

<!-- 更新操作 -->

<update id="updateUser" parameterType="com.example.entity.User">code>

UPDATE users

SET username = #{username}, password = #{password}, email = #{email}

WHERE id = #{id}

</update>

<!-- 删除操作 -->

<delete id="deleteUser" parameterType="int">code>

DELETE FROM users WHERE id = #{id}

</delete>

</mapper>

解析

namespace:定义 Mapper 的命名空间,通常与 Mapper 接口的全限定名相同。resultMap:用于定义结果集与实体类属性的映射关系。sql:用于定义可重用的 SQL 片段。selectinsertupdatedelete:分别定义查询、插入、更新、删除操作。

4.2 ResultMap

ResultMap 用于将查询结果与实体类属性进行映射。

<resultMap id="userResultMap" type="com.example.entity.User">code>

<id property="id" column="id"/>code>

<result property="username" column="username"/>code>

<result property="password" column="password"/>code>

<result property="email" column="email"/>code>

</resultMap>

id:映射主键字段。result:映射非主键字段。

4.3 SQL 片段

SQL 片段可以提高 SQL 语句的重用性。

<sql id="selectAllFields">code>

id, username, password, email

</sql>

使用 <include> 标签引入 SQL 片段:

<select id="selectUserById" resultMap="userResultMap" parameterType="int">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

WHERE id = #{id}

</select>

4.4 动态 SQL

MyBatis 支持动态 SQL,可以根据不同的条件动态生成 SQL 语句。

使用 <if> 标签

<select id="selectUser" resultMap="userResultMap" parameterType="map">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

WHERE 1=1

<if test="username != null">code>

AND username = #{username}

</if>

<if test="email != null">code>

AND email = #{email}

</if>

</select>

使用 <choose> 标签

<select id="selectUser" resultMap="userResultMap" parameterType="map">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

<where>

<choose>

<when test="username != null">code>

username = #{username}

</when>

<when test="email != null">code>

email = #{email}

</when>

<otherwise>

id = #{id}

</otherwise>

</choose>

</where>

</select>

使用 <foreach> 标签

<select id="selectUsersByIds" resultMap="userResultMap" parameterType="list">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

WHERE id IN

<foreach item="id" collection="list" open="(" separator="," close=")">code>

#{id}

</foreach>

</select>

使用 <trim> 标签

<trim> 标签用于动态地移除多余的 SQL 关键字,如 ANDOR 等。

<select id="selectUser" resultMap="userResultMap" parameterType="map">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

<trim prefix="WHERE" prefixOverrides="AND |OR ">code>

<if test="username != null">code>

AND username = #{username}

</if>

<if test="email != null">code>

AND email = #{email}

</if>

</trim>

</select>

使用<set> 标签

<set> 标签用于动态生成 SET 子句,用于 UPDATE 语句中。

<update id="updateUser" parameterType="com.example.entity.User">code>

UPDATE users

<set>

<if test="username != null">code>

username = #{username},

</if>

<if test="password != null">code>

password = #{password},

</if>

<if test="email != null">code>

email = #{email}

</if>

</set>

WHERE id = #{id}

</update>

使用 <where> 标签

<where> 标签用于动态生成 WHERE 子句,会自动去除多余的 ANDOR 等关键字。

<select id="selectUser" resultMap="userResultMap" parameterType="map">code>

SELECT <include refid="selectAllFields"/>code>

FROM users

<where>

<if test="username != null">code>

AND username = #{username}

</if>

<if test="email != null">code>

AND email = #{email}

</if>

</where>

</select>


5. Mapper 接口

Mapper 接口是 MyBatis 的核心组件之一,它定义了数据库操作的方法。

5.1 接口定义

package com.example.mapper;

import com.example.entity.User;

import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

@Insert("INSERT INTO users (username, password, email) VALUES (#{username}, #{password}, #{email})")

@Options(useGeneratedKeys = true, keyProperty = "id")

void insertUser(User user);

@Select("SELECT * FROM users WHERE id = #{id}")

User selectUserById(@Param("id") int id);

@Select("SELECT * FROM users")

List<User> selectAllUsers();

@Update("UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")

void updateUser(User user);

@Delete("DELETE FROM users WHERE id = #{id}")

void deleteUser(@Param("id") int id);

}

5.2 注解配置

@Insert:定义插入 SQL 语句。@Select:定义查询 SQL 语句。@Update:定义更新 SQL 语句。@Delete:定义删除 SQL 语句。@Options:用于配置附加选项,如自动生成主键。@Param:用于指定参数名称,解决参数名不匹配的问题。

5.3 返回值类型

Mapper 接口方法的返回值类型可以是以下几种:

单一对象:查询返回单个结果。集合:查询返回多个结果。基本类型:返回单个字段值,如 intString 等。void:用于插入、更新、删除操作。

5.4 参数传递

Mapper 接口方法的参数可以是以下几种:

单个参数

如基本类型、对象。

多个参数:可以使用 @Param 注解指定参数名称。集合参数:如 ListMap 等。


6. MyBatis 与 Spring 整合

MyBatis 可以与 Spring 框架无缝整合,通过 Spring 管理 MyBatis 的 SqlSessionFactory 和事务。

6.1 Spring 配置文件

数据源配置

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">code>

<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>code>

<property name="url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC"/>code>

<property name="username" value="root"/>code>

<property name="password" value="password"/>code>

</bean>

SqlSessionFactory 配置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">code>

<property name="dataSource" ref="dataSource"/>code>

<property name="mapperLocations" value="classpath*:com/example/mapper/*.xml"/>code>

</bean>

Mapper 接口扫描

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">code>

<property name="basePackage" value="com.example.mapper"/>code>

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>code>

</bean>

6.2 Spring Java 配置

使用 Java 配置替代 XML 配置:

package com.example.config;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

@Configuration

@MapperScan(basePackages = "com.example.mapper")

public class MyBatisConfig {

@Bean

public DataSource dataSource() {

DriverManagerDataSource dataSource = new DriverManagerDataSource();

dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

dataSource.setUrl("jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC");

dataSource.setUsername("root");

dataSource.setPassword("password");

return dataSource;

}

@Bean

public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {

SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();

sessionFactory.setDataSource(dataSource);

sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/example/mapper/*.xml"));

return sessionFactory.getObject();

}

}

6.3 事务管理

配置事务管理器

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">code>

<property name="dataSource" ref="dataSource"/>code>

</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>code>

声明式事务管理

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import com.example.mapper.UserMapper;

import com.example.entity.User;

@Service

public class UserService {

@Autowired

private UserMapper userMapper;

@Transactional

public void createUser(User user) {

userMapper.insertUser(user);

}

@Transactional(readOnly = true)

public User getUserById(int id) {

return userMapper.selectUserById(id);

}

}


7. MyBatis 进阶特性

7.1 缓存机制

MyBatis 提供了一级缓存和二级缓存机制,以提高性能。

一级缓存

作用范围:SqlSession 级别。特性:默认开启,不同 SqlSession 之间不共享。

二级缓存

作用范围:Mapper 级别。特性:需要显式配置,可在不同 SqlSession 之间共享。

<cache/>

自定义缓存

MyBatis 支持自定义缓存,可以通过实现 Cache 接口来自定义缓存行为。

package com.example.cache;

import org.apache.ibatis.cache.Cache;

import java.util.concurrent.locks.ReadWriteLock;

public class CustomCache implements Cache {

private final String id;

public CustomCache(String id) {

this.id = id;

}

@Override

public String getId() {

return this.id;

}

@Override

public void putObject(Object key, Object value) {

// 自定义缓存实现

}

@Override

public Object getObject(Object key) {

// 自定义缓存实现

return null;

}

@Override

public Object removeObject(Object key) {

// 自定义缓存实现

return null;

}

@Override

public void clear() {

// 自定义缓存实现

}

@Override

public int getSize() {

// 自定义缓存实现

return 0;

}

@Override

public ReadWriteLock getReadWriteLock() {

return null;

}

}

配置自定义缓存:

<cache type="com.example.cache.CustomCache"/>code>

7.2 分页插件

MyBatis 不支持分页功能,但可以通过插件实现分页。

<plugins>

<plugin interceptor="com.example.plugin.PaginationInterceptor">code>

<property name="dialect" value="mysql"/>code>

</plugin>

</plugins>

分页插件示例:

package com.example.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.plugin.*;

import java.sql.Connection;

import java.util.Properties;

@Intercept

s({

@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class})

})

public class PaginationInterceptor implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

// 拦截逻辑

return invocation.proceed();

}

@Override

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

// 设置属性

}

}

7.3 拦截器

MyBatis 支持插件机制,可以通过拦截器实现自定义功能,如分页、日志记录等。

自定义拦截器

package com.example.interceptor;

import org.apache.ibatis.plugin.*;

import java.util.Properties;

@Intercepts({

@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class})

})

public class MyInterceptor implements Interceptor {

@Override

public Object intercept(Invocation invocation) throws Throwable {

// 拦截逻辑

return invocation.proceed();

}

@Override

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

// 设置属性

}

}

配置自定义拦截器:

<plugins>

<plugin interceptor="com.example.interceptor.MyInterceptor">code>

<property name="propertyName" value="propertyValue"/>code>

</plugin>

</plugins>

7.4 日志拦截器

MyBatis 提供了日志拦截器,可以用于记录 SQL 语句执行的详细信息。

配置日志拦截器:

<settings>

<setting name="logImpl" value="LOG4J"/>code>

</settings>

使用 Log4j 记录 SQL 日志:

log4j.rootLogger=DEBUG, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %c{1} - %m%n

7.5 MyBatis Generator

MyBatis Generator 是一个代码生成工具,可以根据数据库表结构生成实体类、Mapper 接口和 Mapper XML 文件。

配置文件示例

<?xml version="1.0" encoding="UTF-8"?>code>

<!DOCTYPE generatorConfiguration

PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"

"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

<context id="DB2Tables" targetRuntime="MyBatis3">code>

<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"code>

connectionURL="jdbc:mysql://localhost:3306/testdb"code>

userId="root"code>

password="password"/>code>

<javaModelGenerator targetPackage="com.example.entity" targetProject="src/main/java"/>code>

<sqlMapGenerator targetPackage="com.example.mapper" targetProject="src/main/resources"/>code>

<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"/>code>

<table tableName="users" domainObjectName="User"/>code>

</context>

</generatorConfiguration>

运行 MyBatis Generator

可以通过命令行或 Maven 插件运行 MyBatis Generator。

命令行运行

java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml -overwrite

Maven 插件运行

pom.xml 中添加插件配置:

<build>

<plugins>

<plugin>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-maven-plugin</artifactId>

<version>1.4.0</version>

<executions>

<execution>

<id>generate-sources</id>

<goals>

<goal>generate</goal>

</goals>

</execution>

</executions>

<configuration>

<verbose>true</verbose>

<overwrite>true</overwrite>

</configuration>

</plugin>

</plugins>

</build>

执行 Maven 命令:

mvn mybatis-generator:generate


8. 总结

MyBatis 是一款强大的持久层框架,提供了灵活的 SQL 映射和强大的动态 SQL 功能,能够与 Spring 框架无缝整合,支持缓存、插件、拦截器等高级特性,是 Java 开发中常用的持久层解决方案之一。

通过本文档的介绍,您可以掌握 MyBatis 的基本使用方法和高级特性,能够在项目中灵活运用 MyBatis 进行数据库操作。

参考资料

MyBatis 官方文档Spring 官方文档MyBatis Generator 官方文档


以上是关于 MyBatis 的详细介绍和使用指南,希望能够帮助您更好地理解和使用 MyBatis。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。