MyBatis:一文带你全面了解

文章来源:MyBatis:一文带你全面了解 - 知乎

MyBatis:一文带你全面了解

1. 概述

1.1 MyBatis简介

MyBatis是一个基于Java语言的持久层框架,它通过XML描述符或注解将对象与存储过程或SQL语句进行映射,并提供了普通SQL查询、存储过程和高级映射等操作方式,使得操作数据库变得非常方便。

MyBatis是Apache下的一个开源项目,其前身是iBATIS,它在2002年由Clinton Begin首次发布。2010年5月,该项目由iBATIS更名为MyBatis,同时推出了第一版MyBatis 3,在整个持久层框架市场上引起了很大的关注和广泛的应用。

1.2 MyBatis历史演变

在iBATIS项目中,XML描述符是核心并且是唯一的形式,它为开发人员提供了很大的灵活性,然而也产生了一些问题,如繁琐、容易出错等。

在MyBatis 3中,Mapper接口和注解成为了主流的配置方式,XML描述符仍然被支持,但不再是唯一的形式。同时,MyBatis 3大量采用了Java 5.0注解,使得代码更加简洁明了。

1.3 MyBatis的优点和局限性

1.3.1 优点

  • 灵活性高:MyBatis不会对应用程序或数据库的现有设计强加任何影响,开发人员可以使用他们已经熟悉的SQL语句、存储过程和数据库触发器等
  • SQL可控性强:对于复杂查询和多表关联查询时,MyBatis的优势尤为明显,因为可以更加灵活地控制生成的SQL语句,并在需要的情况下针对不同的数据库实现进行优化。
  • 缓存机制好:MyBatis提供了一级缓存和二级缓存,可以有效地减少数据库访问次数,提高响应速度,而且它们的使用非常方便,并且默认情况下处于开启状态。
  • 生态系统完善:MyBatis有着非常强大的社区支持,同时它也与Spring,Spring Boot等流行框架或中间件无缝整合,方便企业级项目的开发。

1.3.2 局限性

  • 性能问题:相比于Hibernate等ORM框架,在大规模数据处理能力和并发性方面,MyBatis的性能表现略逊一筹。
  • 配置复杂:相比Hibernate等ORM框架,MyBatis的配置文件相对较为复杂,需要花费更多的时间和精力进行配置。
  • 映射错误难以追踪:由于映射文件是通过XML描述符或注解进行的,为了解决常见的SQL问题,需要对SQL语句的编写和映射文件的正确描述非常敏感,出现异常时排查起来也较为繁琐。

2. 入门指南

2.1 安装和配置MyBatis

2.1.1 MyBatis的安装

MyBatis的安装十分简单,只需要在项目的pom.xml文件中添加如下依赖即可:

<dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version></dependency>
</dependencies>

另外,如果需要使用MyBatis Generator来自动生成Java代码和MyBatis映射文件,则还需要添加如下插件:

<plugins><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.4.0</version><configuration><!-- MyBatis Generator配置文件的位置 --><configurationFile>src/main/resources/generatorConfig.xml</configurationFile><overwrite>true</overwrite><verbose>true</verbose></configuration></plugin>
</plugins>

2.1.2 数据库连接池的选择

MyBatis并没有内置数据库连接池,因此需要使用第三方的数据库连接池。常见的数据库连接池有如下几种:

  • HikariCP:性能最好的连接池,也是目前最流行的连接池之一。
  • Apache Commons DBCP2:Apache官方开发的连接池,支持连接池配置和管理、连接有效性验证、闲置连接回收等功能。
  • Alibaba Druid:阿里巴巴开发的连接池,支持JDBC规范、多数据源、SQL防注入、监控等功能。

在实际使用中,我们可以根据自己的需求选择合适的数据库连接池,这里以HikariCP为例进行演示。可以通过以下方式添加HikariCP的依赖:

<dependencies><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>4.0.3</version></dependency>
</dependencies>

2.1.3 MyBatis配置方式

2.1.3.1 基于xml配置文件配置

MyBatis的配置文件是一个XML文件,包含了MyBatis的大部分配置信息,例如数据库连接信息、映射文件位置、缓存配置等。下面是一个简单的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="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="com.zaxxer.hikari.HikariDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useSSL=false&amp;serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!-- 扫描映射文件 --><mappers><mapper resource="com/example/demo/mapper/UserMapper.xml"/></mappers>
</configuration>

其中,<environments>标签用于指定数据库连接信息,包括事务管理器和数据源信息。这里使用了HikariCP来作为数据源,同时指定了MySQL数据库的连接信息。

<mappers>标签用于指定映射文件的位置,这里指定了一个映射文件,并指定了它的资源路径。

2.1.3.2 基于yaml配置文件配置

MyBatis也支持使用YAML格式来进行配置,相对于XML格式更加简洁直观。以下是一个基于YAML格式配置的样例:

# MyBatis 配置
mybatis:# 别名配置typeAliasesPackage: com.example.demo.entity# Mapper XML文件存放路径mapperLocations: classpath*:mapper/*.xml# 数据库连接池配置datasource:url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: rootdriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSourcehikari:minimumIdle: 5maximumPoolSize: 20idleTimeout: 300000connectionTimeout: 30000

其中,typeAliasesPackage用于设置实体类的包路径,mapperLocations用于指定Mapper XML文件的位置,datasource用于配置数据库连接池,可以设置连接池的参数。

2.1.3.3 基于注解配置文配置

在MyBatis中,还可以使用注解来进行配置,不再需要XML或YAML格式的配置文件。以下是一个基于注解的样例:

// 实体类
public class User {private Long id;private String username;private Integer age;// getter、setter方法省略
}// Dao接口
@Mapper
public interface UserDao {@Select("SELECT * FROM user WHERE id = #{id}")User findById(Long id);@Insert("INSERT INTO user(username, age) VALUES (#{username}, #{age})")int save(User user);@Update("UPDATE user SET username = #{username}, age = #{age} WHERE id = #{id}")int update(User user);@Delete("DELETE FROM user WHERE id = #{id}")int deleteById(Long id);
}// 配置类
@Configuration
@MapperScan("com.example.demo.mapper")
public class MybatisConfig {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();sessionFactory.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));return sessionFactory.getObject();}
}

以上代码使用了@Mapper注解来标识Dao接口,并使用@Select@Insert@Update@Delete等注解来进行SQL操作的配置。在MybatisConfig中,使用了@MapperScan注解来指定Mapper类的扫描路径,并使用SqlSessionFactoryBean来进行SqlSessionFactory的配置。

2.2 如何使用MyBatis

2.2.1 基础CRUD操作

2.2.1.1 映射文件的编写

MyBatis的核心是SQL映射语句,而SQL映射语句则是以XML文件的形式维护在项目中。以下是一个简单的MyBatis映射文件的示例:

<?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.example.demo.mapper.UserMapper"><!-- 查询操作 --><select id="findById" parameterType="Long" resultType="User">SELECT *FROM userWHERE id = #{id}</select><!-- 插入操作 --><insert id="save" parameterType="User">INSERT INTO user(username, age)VALUES (#{username}, #{age})</insert><!-- 更新操作 --><update id="update" parameterType="User">UPDATE user SETusername = #{username},age = #{age}WHERE id = #{id}</update><!-- 删除操作 --><delete id="deleteById" parameterType="Long">DELETE FROM userWHERE id = #{id}</delete>
</mapper>

其中,<mapper>标签用于定义命名空间,这里指定了com.example.demo.mapper.UserMapper作为命名空间。

<select>标签用于定义查询操作,id属性表示该SQL语句的唯一标识,parameterType表示参数类型,resultType表示返回值类型。在这个例子中,findById是查询用户信息的语句。

<insert><update><delete>标签分别表示插入、更新和删除操作,它们的语法与<select>标签类似。

2.2.1.2 Java代码的编写

有了映射文件之后,我们就可以使用Java代码来进行数据库操作了。

public interface UserMapper {// 查询操作User findById(Long id);// 插入操作int save(User user);// 更新操作int update(User user);// 删除操作int deleteById(Long id);
}

首先定义一个接口,其中包含几个基本的CRUD操作。

@Mapper
public interface UserMapper {User findById(Long id);int save(User user);int update(User user);int deleteById(Long id);
}

然后使用@Mapper注解标识该接口为Mapper接口。MyBatis会自动扫描这些接口并创建对应的实现类。

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User findById(Long id) {return userMapper.findById(id);}@Overridepublic int save(User user) {return userMapper.save(user);}@Overridepublic int update(User user) {return userMapper.update(user);}@Overridepublic int deleteById(Long id) {return userMapper.deleteById(id);}
}

最后,在Service层中注入Mapper接口,并使用对应的方法进行数据库操作。

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

在Spring Boot主类中加上@MapperScan注解,指定Mapper接口的扫描路径。

通过以上步骤,我们就可以完成基本的CRUD操作。

2.2.2 动态SQL语句的编写

在实际开发中,我们需要根据不同的条件动态生成SQL语句,这时就需要使用MyBatis提供的动态SQL功能。常用的动态SQL元素有ifwhereforeach等。

2.2.2.1 if元素

if元素可以用于根据条件判断是否包含某个SQL语句片段。

例如,我们需要查询年龄大于18岁且小于等于30岁的用户信息,可以这样编写SQL语句:

<select id="findUsersByAge" parameterType="Map" resultType="User">SELECT *FROM userWHERE 1 = 1<if test="minAge != null">AND age &gt;= #{minAge}</if><if test="maxAge != null">AND age &lt;= #{maxAge}</if>
</select>

以上代码中,<if>元素用于判断minAgemaxAge是否为null,如果不为null,则将对应的SQL语句片段拼接到最终的SQL语句中。

2.2.2.2 where元素

where元素可以用于动态生成WHERE子句,如果所有条件均为null,则不会生成WHERE子句。

例如,我们需要查询用户名和密码匹配的用户信息,可以这样编写SQL语句:

<select id="findUserByUsernameAndPassword" parameterType="Map" resultType="User">SELECT *FROM user<where><if test="username != null">AND username = #{username}</if><if test="password != null">AND password = #{password}</if></where>
</select>

以上代码中,<where>元素用于动态生成WHERE子句,如果usernamepassword均为null,则不会生成WHERE子句。

2.2.2.3 foreach元素

foreach元素可以用于循环遍历一个集合,并将集合中的元素拼接到SQL语句中。

例如,我们需要查询多个用户信息,可以这样编写SQL语句:

<select id="findUsersByIds" parameterType="List" resultType="User">SELECT *FROM userWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

以上代码中,<foreach>元素用于循环遍历List类型的参数,并将集合中的元素拼接到SQL语句中。

2.2.3 插入操作

在MyBatis中,插入操作分为手动指定ID和数据库自动生成ID两种方式。

2.2.3.1 手动指定ID

如果需要手动指定ID,可以这样编写SQL语句:

<insert id="save" parameterType="User">INSERT INTO user(id, username, password, age)VALUES (#{id}, #{username}, #{password}, #{age})
</insert>

以上代码中,将插入的ID值直接作为参数传入插入语句中。

2.2.3.2 数据库自动生成ID

如果需要数据库自动生成ID,可以这样编写SQL语句:

<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(username, password, age)VALUES (#{username}, #{password}, #{age})
</insert>

以上代码中,通过useGeneratedKeys="true"参数告诉MyBatis要求数据库生成主键,并通过keyProperty="id"参数指定了主键的属性名。这样,当执行插入操作后,主键值将自动赋值到User对象的id属性中。

2.2.4 删除操作

删除操作比较简单,可以这样编写SQL语句:

<delete id="deleteById" parameterType="Long">DELETE FROM userWHERE id = #{id}
</delete>

以上代码中,直接通过传入的id参数进行删除操作。

2.2.5 更新操作

更新操作也比较简单,可以这样编写SQL语句:

<update id="update" parameterType="User">UPDATE user SETusername = #{username},password = #{password},age = #{age}WHERE id = #{id}
</update>

以上代码中,通过传入的User对象进行更新操作。

2.2.6 多表关联查询

在实际应用中,常常需要进行多表关联查询,MyBatis提供了<association><collection>标签来完成多表关联查询。

2.2.6.1 一对一关联查询

例如,我们有两个表,分别是user表和card表,每个用户都有一张银行卡,通过userId列可以进行关联查询。可以这样编写SQL语句:

<select id="findUsersWithCards" resultType="User">SELECT u.*, c.*FROM user uINNER JOIN card c ON u.id = c.userId
</select>

以上代码中,通过INNER JOIN连接两个表,并使用u.*c.*来选择需要查询的列。

然而,直接返回结果集会将所有数据都映射到User对象中,并不符合我们的需求。此时,可以使用<resultMap>标签来自定义结果映射规则。

<resultMap id="userMap" type="User"><id property="id" column="id"/><result property="username" column="username"/><result property="password" column="password"/><result property="age" column="age"/><association property="card" javaType="Card"><id property="id" column="cardId"/><result property="cardCode" column="cardCode"/><result property="balance" column="balance"/><result property="userId" column="userId"/></association>
</resultMap><select id="findUsersWithCards" resultMap="userMap">SELECT u.id, u.username, u.password, u.age, c.id AS cardId, c.cardCode, c.balance, c.userIdFROM user uINNER JOIN card c ON u.id = c.userId
</select>

以上代码中,<resultMap>标签定义了结果映射规则,包括主键、普通属性和关联属性。其中,<association>标签指定了一个一对一关联关系,并通过property属性指向User对象中的Card属性。

2.2.6.2 一对多关联查询

例如,我们有两个表,分别是user表和address表,每个用户可以有多个地址,通过userId列可以进行关联查询。可以这样编写SQL语句:

<select id="findUsersWithAddresses" resultMap="userMap">SELECT u.*, a.*FROM user uINNER JOIN address a ON u.id = a.userId
</select>

以上代码中,同样使用INNER JOIN连接两个表。

然而,直接返回结果集会将所有数据都映射到User对象中,并不符合我们的需求。此时,可以使用<collection>标签来指定一个一对多关联关系,并通过property属性指向User对象中的addresses属性。

<resultMap id="userMap" type="User"><id property="id" column="id"/><result property="username" column="username"/><result property="password" column="password"/><result property="age" column="age"/><collection property="addresses" ofType="Address"><id property="id" column="id"/><result property="address" column="address"/><result property="userId" column="userId"/></collection>
</resultMap><select id="findUsersWithAddresses" resultMap="userMap">SELECT u.id, u.username, u.password, u.age, a.id, a.address, a.userIdFROM user uINNER JOIN address a ON u.id = a.userId
</select>

以上代码中,<collection>标签指定了一个一对多关联关系,并通过ofType属性指定目标类型为Address

2.2.7 一些高级功能

2.2.7.1 缓存机制

MyBatis提供了缓存机制,这可以有效地减少与数据库的交互次数,提高系统性能。

默认情况下,MyBatis会开启一级缓存(SqlSession级别的缓存)和二级缓存(全局共享的缓存)。

<!-- 配置全局二级缓存 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

以上代码中,通过配置<cache>标签来启用全局二级缓存,并指定了使用Ehcache作为缓存实现方式。

在自定义Mapper接口中,可以通过@CacheNamespace注解来启用单独的二级缓存。

@CacheNamespace
public interface UserMapper {// ...
}

以上代码中,通过@CacheNamespace注解来启用UserMapper的二级缓存。

需要注意的是,如果在进行insert、update、delete等操作时,MyBatis会清空该namespace下的所有缓存。

2.2.7.2 分页插件

MyBatis提供了分页插件,这可以方便地实现分页查询功能。

首先,引入分页插件jar包,例如使用PageHelper插件:

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.11</version>
</dependency>

然后,在MyBatis配置文件中配置分页插件:

<plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

最后,在自定义Mapper接口方法中使用分页插件来完成分页查询:

public interface UserMapper {List<User> findUsersByPage(@Param("pageNum") int pageNum, @Param("pageSize") int pageSize);
}

以上代码中,使用@Param注解来指定传入的参数名,使用PageHelper插件来实现分页查询。

@Test
public void testFindUsersByPage() {SqlSession sqlSession = sqlSessionFactory.openSession();try {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);PageHelper.startPage(2, 3);List<User> users = userMapper.findUsersByPage();for (User user : users) {System.out.println(user);}PageInfo<User> pageInfo = new PageInfo<>(users);System.out.println("当前页:" + pageInfo.getPageNum());System.out.println("每页记录数:" + pageInfo.getPageSize());System.out.println("总记录数:" + pageInfo.getTotal());System.out.println("总页数:" + pageInfo.getPages());System.out.println("是否第一页:" + pageInfo.isIsFirstPage());System.out.println("是否最后一页:" + pageInfo.isIsLastPage());} finally {sqlSession.close();}
}

以上代码中,使用PageHelper.startPage()方法来指定分页查询的页码和每页记录数,使用PageInfo类来获取分页相关信息。

3. MyBatis的高级应用

3.1 MyBatis整合Spring

3.1.1 原理

MyBatis和Spring的整合,可以通过Spring提供的SqlSessionFactoryBeanMapperScannerConfigurer来实现。

其中,SqlSessionFactoryBean负责创建SqlSessionFactory对象,MapperScannerConfigurer负责将Mapper接口扫描注册到Spring容器中,以便在应用中注入并使用。

3.1.2 配置方法

首先,引入MyBatis和Spring相关jar包,例如:

<!-- MyBatis -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.7</version>
</dependency>
<!-- Spring -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.6</version>
</dependency>

然后,在Spring配置文件中配置SqlSessionFactoryBeanMapperScannerConfigurer

<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean><!-- 注册Mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.example.mapper"/>
</bean>

以上代码中,通过SqlSessionFactoryBean配置SqlSessionFactory对象,并指定数据源和MyBatis配置文件路径。通过MapperScannerConfigurer注册Mapper接口,其中basePackage指定了Mapper接口所在的包路径。

最后,在需要使用Mapper接口的地方,注入该接口,并使用。

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User getUserById(int id) {return userMapper.getUserById(id);}
}

以上代码中,通过@Autowired注解将UserMapper接口注入到UserServiceImpl中,并在方法实现中使用该接口。

3.2 MyBatis Generator——自动化生成MyBatis代码

3.2.1 基础原理

MyBatis Generator是MyBatis官方提供的一个开源项目,可以根据数据库表自动生成对应的Java实体类、Mapper接口和XML文件,极大地简化了开发工作。

具体来说,MyBatis Generator会根据指定的数据库连接信息、表名规则、生成策略等参数,自动生成Java实体类和Mapper接口。同时,还可以根据表结构自动生成SQL语句,并将其配置到XML文件中,以便直接使用。

3.2.2 文件生成策略

通常情况下,MyBatis Generator会为每个表生成3个文件:

  1. Java实体类文件:默认位于src/main/java下的指定包路径中。
  2. Mapper接口文件:默认位于src/main/java下的指定包路径中。
  3. XML文件:默认位于src/main/resources下的指定包路径中。

在生成XML文件时,MyBatis Generator提供了不同的生成策略,如:

  1. XML文件与Java类放在一起,以*Mapper.xml命名。
  2. XML文件单独放置,以*Mapper.xml命名,并在Java类中通过@Mapper注解来指定XML文件名。

3.2.3 使用方式

首先,引入MyBatis Generator相关jar包和数据库驱动,例如:

<!-- MyBatis Generator -->
<dependency><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-core</artifactId><version>1.4.0</version>
</dependency>
<!-- MySQL驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.24</version>
</dependency>

然后,在项目根目录下新建一个generatorConfig.xml配置文件,其中包含数据库连接信息、表名规则、生成策略、Java类型映射等。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration><!-- 数据库连接信息 --><context id="mysql" targetRuntime="MyBatis3"><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/test?useSSL=false&amp;serverTimezone=UTC"userId="root"password="root"/><!-- 表名规则 --><table tableName="user" domainObjectName="User"/><!-- Java类型映射 --><javaTypeResolver><property name="forceBigDecimals" value="false"/></javaTypeResolver><!-- Mapper接口生成策略 --><javaClientGenerator targetPackage="com.example.mapper"targetProject="src/main/java"type="XMLMAPPER"/><!-- XML文件生成策略 --><sqlMapGenerator targetPackage="mapper"targetProject="src/main/resources"type="XML"/><!-- Java实体类生成策略 --><javaModelGenerator targetPackage="com.example.entity"targetProject="src/main/java"enableSubPackages="true"xmlAccessor="PUBLIC"/><!-- 表字段和Java属性名映射 --><tableFieldOverride column="id" property="id" /><tableFieldOverride column="username" property="username" /><tableFieldOverride column="password" property="password" /><tableFieldOverride column="age" property="age" /></context>
</generatorConfiguration>

以上代码中,通过jdbcConnection指定数据库连接信息。使用<table>标签指定要生成Java实体类和Mapper接口的表,其中tableName指定表名,domainObjectName指定Java实体类名。

通过<javaTypeResolver>指定Java类型映射,例如将bigint映射到Long类型。通过<javaClientGenerator><sqlMapGenerator><javaModelGenerator>分别设置Java实体类生成路径、Mapper接口生成路径和XML文件生成路径及文件名。

最后,在根目录下打开命令行窗口,执行以下命令即可自动生成代码:

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

其中,mybatis-generator-core-x.x.x.jar为MyBatis Generator的jar包。-configfile参数指定配置文件名,-overwrite参数表示覆盖已有文件。执行完成后,即可在指定路径下看到生成的代码文件。

示例:使用MyBatis Generator生成Java实体类、Mapper接口和XML文件,假设有一张名为account的表,该表包含id、name和balance三个字段。

首先,在generatorConfig.xml配置文件中添加以下代码:

<context id="mysql" targetRuntime="MyBatis3"><!-- ... --><!-- 表名规则 --><table tableName="account" domainObjectName="Account"/><!-- ... -->
</context>

然后,在根目录下打开命令行窗口,执行以下命令:

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

执行完成后,可以在指定路径下看到生成的代码文件:

  1. Java实体类:com.example.entity.Account.java
  2. Mapper接口:com.example.mapper.AccountMapper.java
  3. XML文件:mapper/AccountMapper.xml

3.3 代码生成器:MyBatis Plus

3.3.1 MyBatis Plus简介

MyBatis Plus是一款MyBatis框架的增强工具,提供了很多实用的功能,可以极大地简化开发工作。其主要功能包括:

  • 支持CRUD操作:提供了通用的Mapper接口及其实现,可以减少Mapper接口的编写,简化CRUD操作。
  • 自动生成代码:提供了代码生成器,可以根据数据库表自动生成对应的Java实体类、Mapper接口及其XML文件。
  • Lambda查询:提供了Lambda表达式查询功能,可以更方便地进行复杂的查询。
  • 分页查询:提供了分页插件,可以轻松实现分页查询。
  • 多租户支持:提供了多租户插件,可以支持多租户场景。

3.3.2 MyBatis Plus的优点和局限性

MyBatis Plus的主要优点有:

  1. 简化开发:提供了很多实用的功能,并且用法简单,可以极大地简化开发工作。
  2. 提高效率:提供了自动生成代码的功能,可以提高开发效率。
  3. 提高代码可读性:使用Lambda表达式进行查询,可以使查询语句更加直观易懂。
  4. 提高代码可维护性:提供了通用的Mapper接口及其实现,可以减少Mapper接口的编写,简化CRUD操作。

MyBatis Plus的局限性主要有:

  1. 依赖关系较强:需要整合Spring框架和MyBatis框架,对于初学者来说可能不易上手。
  2. 自动化生成的代码不够灵活:自动生成的代码一般不适合特定的需求,需要根据实际需要进行二次开发。

3.3.3 MyBatis Plus的使用

3.3.3.1 引入MyBatis Plus

首先,在项目中引入MyBatis Plus相关的jar包:

<!-- MyBatis Plus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.5</version>
</dependency>

3.3.3.2 自动生成代码

MyBatis Plus提供了官方的代码生成器,可以根据数据库表自动生成Java实体类、Mapper接口及其XML文件。使用方法如下:

  1. application.properties配置文件中,添加以下代码:

```properties # 数据库连接信息 spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root

# MyBatis Plus配置 mybatis-plus.mapper-locations=classpath:mapper/*.xml mybatis-plus.type-aliases-package=com.example.entity mybatis-plus.configuration.cache-enabled=false mybatis-plus.global-config.id-type=auto ```

其中,spring.datasource.url为数据库连接信息,mybatis-plus.mapper-locations表示Mapper接口对应的XML文件所在路径,mybatis-plus.type-aliases-package表示Java实体类所在包路径。

  1. 创建代码生成器,并配置相应参数,例如:

```java public class CodeGenerator { public static void main(String[] args) { // 数据源配置 DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setDbType(DbType.MYSQL) .setUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8") .setUsername("root") .setPassword("root") .setDriverName("com.mysql.cj.jdbc.Driver");

// 全局配置GlobalConfig globalConfig = new GlobalConfig();globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java").setAuthor("binjie09").setOpen(false).setFileOverride(true).setIdType(IdType.AUTO).setBaseResultMap(true).setBaseColumnList(true);// 包配置PackageConfig packageConfig = new PackageConfig();packageConfig.setParent("com.example").setEntity("entity").setMapper("mapper").setXml("mapper.xml").setService("service").setServiceImpl("service.impl").setController("controller");// 策略配置StrategyConfig strategyConfig = new StrategyConfig();strategyConfig.setCapitalMode(true).setNaming(NamingStrategy.underline_to_camel).setTablePrefix("t_").setInclude("user");// 代码生成器AutoGenerator autoGenerator = new AutoGenerator();autoGenerator.setDataSource(dataSourceConfig).setGlobalConfig(globalConfig).setPackageInfo(packageConfig).setStrategy(strategyConfig).execute();}

} ```

其中,dataSourceConfig为数据源配置,globalConfig为全局配置,packageConfig为包配置,strategyConfig为策略配置。创建了相应的配置后,调用AutoGeneratorexecute()方法即可生成代码。

在以上代码中,setInclude("user")表示只生成user表对应的Java实体类、Mapper接口及其XML文件,也可不传入参数表示生成所有表的代码。

  1. 执行代码生成器即可:

```java public class CodeGenerator { public static void main(String[] args) { // ...

// 执行生成器autoGenerator.execute();}

} ```

3.3.3.3 Lambda查询

使用MyBatis Plus进行Lambda表达式查询,需要先引入LambdaQueryWrapper类,例如:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

然后,可以进行如下Lambda表达式查询:

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic List<User> listUsersByName(String name) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.like(User::getName, name);return userMapper.selectList(queryWrapper);}
}

以上代码中,queryWrapper.like(User::getName, name)表示查询name字段包含name字符串的记录,而User::getName表示获取User对象的name属性。

3.3.3.4 分页查询

使用MyBatis Plus进行分页查询可以很简单地实现,例如:

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic IPage<User> listUsersByPage(int pageNum, int pageSize) {Page<User> page = new Page<>(pageNum, pageSize);return userMapper.selectPage(page, null);}
}

以上代码中,通过new Page<>(pageNum, pageSize)创建了分页对象,即第pageNum页,每页展示pageSize条数据。然后,调用IPage<User> selectPage(IPage<User> page, Wrapper<User> queryWrapper)方法进行分页查询。

3.3.3.5 多租户支持

MyBatis Plus提供了多租户插件,可以轻松实现多租户场景。首先,在application.yml配置文件中添加以下代码:

mybatis-plus:configuration:# 指定多租户插件类plugins: com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptorglobal-config:# 指定租户ID列名tenant-id-column: tenant_id# 指定租户ID处理器tenant-handler: com.example.handler.MultiTenantHandler

其中,com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor为多租户插件类,tenant-id-column指定租户ID列名,tenant-handler指定租户ID处理器。

然后,在MultiTenantHandler中编写租户ID过滤逻辑,例如:

public class MultiTenantHandler implements TenantHandler {private static final String DEFAULT_TENANT_ID = "1";@Overridepublic Expression getTenantId(boolean where) {// 获取当前租户ID,可以从Session、ThreadLocal等获取String tenantId = TenantContext.getTenantId();// 如果没有获取到租户ID,则使用默认的租户IDif (tenantId == null || "".equals(tenantId.trim())) {tenantId = DEFAULT_TENANT_ID;}return new LongValue(tenantId);}@Overridepublic String getTenantIdColumn() {return "tenant_id";}@Overridepublic boolean doTableFilter(String tableName) {// 过滤掉不需要进行租户ID过滤的表return !"user".equalsIgnoreCase(tableName);}
}

以上代码中,getTenantId()方法用于获取当前租户ID,getTenantIdColumn()方法返回租户ID列名,doTableFilter()方法用于过滤掉不需要进行租户ID过滤的表。

最后,在Mapper接口的查询语句中添加@SqlParser(filter=true)注解即可启用多租户插件,例如:

public class MultiTenantHandler implements TenantHandler {private static final String DEFAULT_TENANT_ID = "1";@Overridepublic Expression getTenantId(boolean where) {// 获取当前租户ID,可以从Session、ThreadLocal等获取String tenantId = TenantContext.getTenantId();// 如果没有获取到租户ID,则使用默认的租户IDif (tenantId == null || "".equals(tenantId.trim())) {tenantId = DEFAULT_TENANT_ID;}return new LongValue(tenantId);}@Overridepublic String getTenantIdColumn() {return "tenant_id";}@Overridepublic boolean doTableFilter(String tableName) {// 过滤掉不需要进行租户ID过滤的表return !"user".equalsIgnoreCase(tableName);}
}

以上代码中,getTenantId()方法用于获取当前租户ID,getTenantIdColumn()方法返回租户ID列名,doTableFilter()方法用于过滤掉不需要进行租户ID过滤的表。

最后,在Mapper接口的查询语句中添加@SqlParser(filter=true)注解即可启用多租户插件,例如:

@Mapper
public interface UserMapper extends BaseMapper<User> {@SqlParser(filter=true)@OverrideList<User> selectList(@Param(Constants.WRAPPER) Wrapper<User> queryWrapper);// ...
}

以上代码中,@SqlParser(filter=true)注解表示启用过滤器,对selectList()方法进行租户ID过滤。

4. 最佳实践

4.1 掌握MyBatis数据表设计

在使用MyBatis进行开发之前,首先要掌握好数据表的设计。好的数据表设计可以极大地提高查询效率,降低系统复杂度。下面介绍一些数据表设计的要点。

4.1.1 表设计规范

  1. 表名命名规范:表名使用小写字母,单词之间用下划线隔开,例如user_info
  2. 字段名命名规范:字段名同样使用小写字母,单词之间用下划线隔开,例如user_id
  3. 主键命名规范:主键名统一使用id
  4. 数据类型选取规范:MySQL支持多种数据类型,需要根据实际情况选择合适的数据类型。对于字符串类型,如果长度不确定,建议选择VARCHAR(255),而不是TEXT或者MEDIUMTEXT
  5. 字段注释规范:为每个字段添加注释,说明该字段的含义、取值范围等。
  6. 索引规范:为常用的查询字段创建索引,可以提高查询效率。但是过多的索引会降低写入性能。

4.1.2 表关系设计

在设计数据表时,需要考虑好表与表之间的关系,包括一对一、一对多和多对多等关系。下面介绍一些常见的表关系设计。

  1. 一对一关系:将主键作为外键,存放在从表中。例如,在一个订单系统中,一个订单只属于一个用户,一个用户只能拥有一个订单,这就是一对一关系。
  2. 一对多关系:将主键作为外键,存放在从表中。例如,在一个博客系统中,一个文章可以有多个评论,一个评论只能属于一个文章,这就是一对多关系。
  3. 多对多关系:需要使用中间表来维护关系,中间表存储两个表的主键,例如,在一个班级系统中,一个学生可以属于多个班级,一个班级可以拥有多个学生,这就是多对多关系。

4.2 映射文件编写的要点

MyBatis的映射文件用于定义SQL语句和映射规则,下面介绍一些映射文件编写的要点。

4.2.1 SQL语句编写

  1. 避免使用SELECT *:应该明确指出需要查询的字段,而不是查询全部字段。
  2. 注意参数类型:在使用Mapper接口调用SQL查询时,参数类型必须与Mapper接口方法参数类型一致或者符合JavaBean规范。
  3. 使用动态SQL:可以根据不同的条件排除或者包含某个SQL片段,以达到避免重复代码的作用。
  4. 使用别名:在多个表或者多个字段具有相同名称时,需要使用别名来消除歧义,提高可读性。

4.2.2 映射规则编写

  1. 映射关系标签:MyBatis提供了很多映射关系标签,例如<resultMap><association><collection>等。需要根据不同情况选择合适的映射关系标签。
  2. 映射ID命名:在定义映射ID时,应该使用规范的命名方式,例如getUserByIdlistUsers等。
  3. 属性配置:可以为映射属性添加配置,例如添加not-null="true"表示该属性不能为空。
  4. 鉴别器:对于一些复杂的查询语句,需要使用鉴别器(discriminator)进行分发处理,示例代码如下:
<resultMap id="userMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><discriminator javaType="int" column="type"><case value="1" resultMap="studentMap"/><case value="2" resultMap="teacherMap"/></discriminator>
</resultMap><resultMap id="studentMap" type="Student"><result column="score" property="score"/>
</resultMap><resultMap id="teacherMap" type="Teacher"><result column="title" property="title"/>
</resultMap>

以上代码中,使用<discriminator>标签进行分发处理,当查询结果中的type列值为1时,使用studentMap映射;当type列值为2时,使用teacherMap映射。

4.3 编写高效的SQL语句

4.3.1 避免使用子查询

子查询指的是将一个SQL语句嵌套到另一个SQL语句中,作为另一个SQL语句的一部分进行查询操作。虽然使用子查询可以实现较为复杂的查询操作,但是也会带来一些性能问题,主要表现在以下几个方面:

  1. 子查询会增加数据库的负载,导致查询速度变慢。
  2. 子查询可能需要重复执行多次,而每次执行都需要进行一次完整的查询操作,从而造成额外的开销。
  3. 子查询可能会引起死锁或者线程阻塞等问题,从而降低数据库的并发性能。

因此,在编写SQL语句时,应该尽可能避免使用子查询。可以通过以下几种方式来替代使用子查询的情况:

  1. 使用多表连接查询:使用多表连接查询可以避免使用子查询,通过将多个表的数据进行连接,完成一次完整的查询操作。
  2. 使用内置函数:数据库提供了许多内置函数,可以用于实现一些高级的查询操作,例如聚合函数、数学函数、字符串函数等。使用内置函数可以减少查询复杂度,避免使用子查询。
  3. 使用临时表:如果必须使用子查询,可以尝试使用临时表。先将子查询的结果存储到一个临时表中,然后在主查询操作中使用该临时表,可以避免子查询多次执行。

以下是一个使用多表连接查询替代子查询的示例:

-- 子查询
SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE name LIKE '%张三%');-- 多表连接查询
SELECT o.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.name LIKE '%张三%';

4.3.2 避免使用%前缀模糊查询

%前缀模糊查询指的是在查询字符串时,使用%作为前缀进行模糊匹配。虽然使用%前缀模糊查询可以实现快速查找匹配字符串的功能,但同时也会带来一些性能问题,主要表现在以下几方面:

  1. %前缀模糊查询会增加数据库的负载,导致查询速度变慢。
  2. %前缀模糊查询可能需要对整个表进行扫描,而这将是一个非常耗时的操作。
  3. %前缀模糊查询不利于索引的使用,因为%前缀模糊查询无法使用普通的B树索引。

因此,在编写SQL语句时,应该尽可能避免使用%前缀模糊查询。可以通过以下方式来替代使用%前缀模糊查询的情况:

  1. 使用全文索引:数据库提供了全文索引功能,可以用于实现全文搜索。使用全文索引可以取代%前缀模糊查询,提高查询效率。
  2. 使用后缀模糊查询:如果必须进行模糊查询,可以尝试使用后缀模糊查询。后缀模糊查询会优先使用索引,从而提高查询效率。
  3. 尽可能精确匹配:在编写查询条件时,应该尽可能精确匹配,避免使用过于宽泛的查询条件。

以下是一个使用全文索引替代%前缀模糊查询的示例:

-- %前缀模糊查询
SELECT * FROM articles WHERE title LIKE '%关键词%';-- 全文索引查询
SELECT * FROM articles WHERE MATCH(title) AGAINST('关键词' IN NATURAL LANGUAGE MODE);

5. 总结

5.1 MyBatis的未来

MyBatis作为一款优秀的ORM框架,其使用广泛,功能也非常强大。MyBatis的未来发展方向主要包括以下几个方面:

  1. 持续推进版本更新:MyBatis在不断地更新和迭代,新版本不仅修复了一些已知问题,还引入了一些新特性和优化,比如在3.x版本中引入了Spring Boot Starter,提供了更便捷的集成方式。
  2. 加强与其他组件的整合:MyBatis可以与Spring Framework等组件紧密配合,形成完善的应用架构。
  3. 多数据源支持:MyBatis在多数据源的支持上也越来越完善,可以针对不同的数据源进行不同的操作。
  4. 引入更多的插件:MyBatis插件是其扩展功能的重要手段,可以在SQL语句执行前、后进行一系列的自定义处理。未来MyBatis可能会开发更多的插件,以提高开发效率和使用体验。

5.2 MyBatis的注意事项以及常见问题

在使用MyBatis时,需要注意以下事项:

  1. 避免使用SELECT *:使用明确指出需要查询的字段,而不是查询全部字段,可以提高查询效率。
  2. 显式指定参数类型:在使用Mapper接口调用SQL查询时,参数类型必须与Mapper接口方法参数类型一致或者符合JavaBean规范。
  3. 使用动态SQL:可以根据不同的条件排除或者包含某个SQL片段,以达到避免重复代码的作用。
  4. 使用缓存:MyBatis支持多种缓存方式,可以提高查询效率。但是需要注意缓存更新策略,以免出现数据不一致问题。
  5. 配置文件管理:MyBatis的配置文件中包含了许多的配置信息,需要注意配置信息的管理和维护。

在使用MyBatis过程中,也存在一些常见问题,例如:

  1. 多表查询使用联合查询的性能问题。
  2. 分页查询时,使用游标或者取所有数据的性能问题。
  3. MyBatis缓存机制带来的数据不一致问题。
  4. SQL语句过于复杂,导致难以调试和维护。
  5. 数据库连接池的优化问题。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.2 内容管理模块 - 课程分类、新增课程、修改课程

内容管理模块-课程分类、新增课程、修改课程 文章目录 内容管理模块-课程分类、新增课程、修改课程一、课程分类1.1 课程分类表1.2 查询树形结构1.2.1 表自连接1.2.2 SQL递归 1.3 Mapper1.4 Service1.5 Controller1.6 效果图 二、添加课程2.1 需求分析2.2 数据表2.2.1 课程基础…

挑战52天学小猪佩奇笔记--day26

52天学完小猪佩奇--day26 ​【本文说明】 本文内容来源于对B站UP 脑洞部长 的系列视频 挑战52天背完小猪佩奇----day26 的视频内容总结&#xff0c;方便复习。强烈建议大家去关注一波UP&#xff0c;配合UP视频学习。 day26的主题&#xff1a;堆雪人 猜台词&#xff1a; 旁白&am…

前端加载访问速度优化(Nginx)

当前端部署文件过大时很容易造成网页加载慢的现象&#xff0c;为了提升加载速度&#xff0c;提供nginx的三种解决方案。 文章目录 1. gzip压缩2. 优化 keepalive 连接3.配置缓存注意事项总结 1. gzip压缩 往nginx.conf 的 http内容段落中加入 # gzip configgzip on;gzip_min_l…

SpringCloudAliBaba篇之Seata:分布式事务组件理论与实践

1、事务简介 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中&#xff0c;一个事务由一组SQL语句组成&#xff0c;事务具有4个属性&#xff1a;原子性、一致性、隔离性、持久性。这四个属性通常称为ACID原则。 原子性(atomici…

wsl minikube的简单应用

minikube minikube官网&#xff1a;https://minikube.sigs.k8s.io/docs/minikube是容器编排与管理的工具&#xff0c;本机事先已经安装了docker进入minikube官网&#xff0c;点Get Started&#xff01;长这熊样 看一硬件要求 CPU 核心数 > 2内存 2GB磁盘 20GB网络连接容器…

微信小程序长按图片识别二维码

设置show-menu-by-longpress"true"即可&#xff0c;长按图片后会弹出一个菜单&#xff0c;若图片中包含二维码或小程序码&#xff0c;菜单中会有响应入口 <image src"图片地址" show-menu-by-longpress"true"></image>官方说明

智能优化算法应用:基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蛾群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蛾群算法4.实验参数设定5.算法结果6.参考文献7.MA…

格密码与线性代数

目录 一. 幺模矩阵 二. Gram-Schmidt 正交化 三. 矩阵分解 四. 格基本区 五. 对偶格基 六. 矩阵伪逆 七. 正定矩阵 八. 矩阵转置 九. 奇异值分解&#xff08;SVD分解&#xff09; 格密码中格基是矩阵&#xff0c;格点是向量。本文章梳理一些格密码常用到的一些线性代数…

bisect_left,bisect_right,bisect的用法,区别以源码分析

bisect_left(*args, **kwargs) 向一个数组插入一个数字&#xff0c;返回应该插入的位置。 如果这个数字不存在于这个数组中&#xff0c;则返回第一个比这个数大的数的索引 如果这个数字存在&#xff0c;则返回数组中这个数的位置的最小值&#xff08;即最左边那个索引&#xf…

【算法系列篇】递归、搜索和回溯(四)

文章目录 前言什么是决策树1. 全排列1.1 题目要求1.2 做题思路1.3 代码实现 2. 子集2.1 题目要求2.2 做题思路2.3 代码实现 3. 找出所有子集的异或总和再求和3.1 题目要求3.2 做题思路3.3 代码实现 4. 全排列II4.1 题目要求4.2 做题思路4.3 代码实现 前言 前面我们通过几个题目…

Vue中父子Props传值不能修改的原因分析以及解决办法

Vue 官方文档中对于 Prop 的定义&#xff1a; Prop 是你可以在组件上注册的一些自定义属性。当一个值传递给一个 props 属性的时候&#xff0c;它就变成了那个组件实例的一个属性。为了给子组件传递数据&#xff0c;我们需要在该组件上使用 v-bind 指令绑定需要传递的数据。 由…

生物识别应用指纹的算法是什么样的?有什么性能?

方案特点 • 采用金融级安全芯片 ACH512 的指纹模组&#xff0c;指纹和密码安全存储&#xff0c;云端数据安全传输 • 采用高性能指纹专用安全MCU芯片ACM32FP4&#xff0c;支持小点阵图像算法处理 • 支持80*64、88*112、96*96、160*160、192*192等像素传感器 • 已适配传…