一、mybatis和mybatisplus的区别
MyBatis 和 MyBatis Plus 都是 Java 中常用的持久层框架,它们有以下一些区别:
1、功能特性
- 基础功能:
-
- MyBatis 是一个传统的 SQL 映射框架,提供了基本的 SQL 映射、结果集映射、动态 SQL 生成等功能。
- MyBatis Plus 在 MyBatis 的基础上进行扩展,提供了更多实用的功能,如内置通用 Mapper、代码生成器、分页插件、性能分析插件等。
- CRUD 操作:
-
- MyBatis 需要手动编写 SQL 语句来实现各种数据库操作,包括增删改查。虽然可以使用动态 SQL 来简化一些常见的操作,但对于一些复杂的查询和操作,可能需要编写较多的 SQL 代码。
- MyBatis Plus 提供了更加便捷的 CRUD 操作方法,例如通过继承 BaseMapper 接口,就可以直接使用内置的方法来实现单表的增删改查操作,大大减少了重复的代码编写工作。
- 分页功能:
-
- MyBatis 本身没有内置的分页功能,需要通过第三方插件或者手动编写分页 SQL 来实现。
- MyBatis Plus 提供了分页插件,可以方便地实现分页查询,只需要传入分页参数,即可自动生成分页 SQL 并进行查询。
2、开发效率
- 代码量:
-
- 使用 MyBatis 进行开发时,对于一些常见的数据库操作,需要编写大量的 SQL 语句和 Java 代码来实现业务逻辑。
- MyBatis Plus 提供了很多便捷的功能和工具,可以大大减少代码量。例如,使用代码生成器可以自动生成实体类、Mapper 接口和 XML 文件,减少了手动编写代码的工作量。
- 开发速度:
-
- 由于 MyBatis Plus 提供了更多的便捷功能和工具,开发人员可以更快地实现业务需求,提高开发速度。
- MyBatis 则需要开发人员手动编写更多的代码,开发速度相对较慢。
3、性能表现
- SQL 执行效率:
-
- MyBatis 和 MyBatis Plus 在 SQL 执行效率上没有明显的差异,它们都将 SQL 语句的执行交给数据库来完成。
- 但是,MyBatis Plus 的分页插件等功能可能会在一定程度上影响性能,因为它需要在查询结果集上进行分页处理。
- 内存占用:
-
- MyBatis 和 MyBatis Plus 的内存占用情况也基本相同,主要取决于数据库连接池的配置和查询结果集的大小。
4、社区支持和生态
- 社区活跃度:
-
- MyBatis 作为一个成熟的框架,拥有庞大的用户群体和活跃的社区。在遇到问题时,可以很容易地在网上找到解决方案。
- MyBatis Plus 作为一个相对较新的框架,社区活跃度也在不断提高。它的开发者团队也在积极维护和更新框架,不断推出新的功能和改进。
- 生态系统:
-
- MyBatis 有很多第三方插件和工具,可以与其他框架和技术进行集成。例如,可以与 Spring、Spring Boot 等框架进行无缝集成。
- MyBatis Plus 也可以与 Spring、Spring Boot 等框架进行集成,并且它的代码生成器等工具也可以与其他工具进行配合使用,形成一个完整的开发生态系统。
二、逻辑删除
在数据库管理中,逻辑删除是一种相对温和的数据处理方式,与物理删除相对应。
1、定义与原理
逻辑删除并非真正从数据库中移除数据,而是通过设置一个特定的字段(通常称为 “删除标志位”)来标记数据为已删除状态。当进行查询操作时,可以通过过滤这个标志位来决定是否显示该数据。例如,设置一个名为 “is_deleted” 的字段,值为 0 表示未删除,值为 1 表示已删除。
2、优点
- 数据可恢复性高
-
- 与物理删除不同,逻辑删除只是对数据进行了标记,实际数据仍然存在于数据库中。如果出现误删情况或者需要恢复历史数据,只需要修改删除标志位即可,大大提高了数据的可恢复性。
- 比如在一个电商系统中,用户不小心删除了订单记录,但如果采用逻辑删除,管理员可以在需要的时候轻松恢复这些订单数据,避免了因误操作带来的严重后果。
- 数据完整性保留
-
- 在一些复杂的业务场景中,数据之间可能存在关联关系。如果进行物理删除,可能会破坏数据的完整性。而逻辑删除可以保持数据的关联关系,确保数据库的整体结构不受影响。
- 例如在一个企业管理系统中,员工信息可能与多个业务模块相关联。如果直接物理删除员工数据,可能会导致其他模块的数据出现错误或不完整。采用逻辑删除则可以避免这种情况的发生。
- 审计与历史记录
-
- 逻辑删除可以方便地进行数据审计和历史记录查询。通过保留已删除数据的状态,可以追踪数据的变化过程,了解数据何时被删除以及删除的原因。
- 对于一些对数据安全性和合规性要求较高的行业,如金融、医疗等,逻辑删除可以满足审计和监管的要求。
3、实现方式
- 数据库层面
-
- 在数据库表设计中,添加一个表示删除状态的字段。在进行删除操作时,更新该字段的值而不是真正删除数据行。
- 可以使用数据库的触发器来自动维护删除标志位,确保数据的一致性。
- 应用程序层面
-
- 在应用程序的业务逻辑中,处理删除操作时,调用数据库的更新语句来设置删除标志位。
- 在查询数据时,根据删除标志位进行过滤,只返回未删除的数据。
4、注意事项
- 数据清理
-
- 虽然逻辑删除可以保留数据,但随着时间的推移,数据库中可能会积累大量已删除的数据,占用存储空间。因此,需要定期对已删除的数据进行清理,以保持数据库的性能。
- 可以制定数据清理策略,例如在一定时间后将真正不再需要的数据进行物理删除。
- 查询性能
-
- 由于查询时需要过滤删除标志位,可能会对查询性能产生一定的影响。特别是在数据量较大的情况下,需要优化查询语句和数据库索引,以提高查询效率。
- 可以根据实际业务需求,合理设计索引,确保查询性能不受影响。
三、简单地增删改查
在软件开发中,简单的增删改查(CRUD)操作是非常基础且频繁使用的功能。以下分别介绍增删改查的基本概念和一般实现方式。
1、增加(Create)
- 定义:向数据库中插入新的数据记录。
- 实现方式:
-
- 在应用程序中,通常通过表单或其他输入方式收集用户输入的数据。
- 然后,将这些数据组装成数据库可以接受的格式,并使用数据库插入语句(如 SQL 的 INSERT 语句)将数据插入到相应的表中。
- 例如,在一个学生管理系统中,要添加一个新的学生记录,可以在界面上输入学生的姓名、年龄、班级等信息,然后通过后端代码将这些信息插入到学生表中。
2、删除(Delete)
- 定义:从数据库中移除指定的数据记录。
- 实现方式:
-
- 一般是根据特定的条件来确定要删除的记录。
- 可以通过记录的唯一标识符(如 ID)或者其他特定的属性值来定位要删除的记录。
- 使用数据库删除语句(如 SQL 的 DELETE 语句)进行删除操作。
- 例如,在一个订单管理系统中,如果要删除一个特定的订单,可以根据订单的 ID 来执行删除操作。
3、修改(Update)
- 定义:更新数据库中已有的数据记录。
- 实现方式:
-
- 首先确定要修改的记录,可以通过与删除操作类似的方式进行定位。
- 然后收集用户输入的新数据,将这些新数据与要修改的记录进行关联。
- 使用数据库更新语句(如 SQL 的 UPDATE 语句)将新数据更新到数据库中。
- 例如,在一个员工信息管理系统中,如果要修改某个员工的工资信息,可以先找到该员工的记录,然后输入新的工资值,最后将新工资值更新到数据库中。
4、查询(Retrieve)
- 定义:从数据库中获取满足特定条件的数据记录。
- 实现方式:
-
- 使用数据库查询语句(如 SQL 的 SELECT 语句)来指定查询条件。
- 查询条件可以是单个属性的值,也可以是多个属性的组合条件。
- 查询结果可以是单个记录,也可以是多个记录的集合。
- 例如,在一个图书管理系统中,可以根据图书的名称、作者、分类等条件进行查询,获取符合条件的图书记录。
四、多租户的处理
在软件系统中,多租户是一种架构模式,允许多个租户(客户或用户组织)共享同一套软件实例,同时保持数据的隔离和安全性。以下是多租户的常见处理方式:
1、数据库层面
- 独立数据库模式:
-
- 为每个租户提供一个独立的数据库。这种方式可以实现最高级别的数据隔离,但成本较高,因为需要管理多个数据库实例。
- 优点是每个租户的数据完全独立,不会受到其他租户的影响,安全性高。如果一个租户的数据出现问题,不会影响其他租户。
- 缺点是管理复杂,需要更多的资源,并且在扩展时可能会面临一些挑战。
- 共享数据库、独立模式:
-
- 多个租户共享同一个数据库,但每个租户有自己独立的模式(表结构)。这种方式在一定程度上降低了成本,同时也能提供较好的数据隔离。
- 优点是可以在一定程度上减少数据库实例的数量,管理相对简单一些。每个租户的数据在逻辑上是独立的,通过不同的表结构进行区分。
- 缺点是如果需要对数据库结构进行更改,可能会影响到所有租户。
- 共享数据库、共享模式:
-
- 所有租户共享同一个数据库和相同的表结构。通过在表中添加一个租户标识符字段来区分不同租户的数据。
- 优点是成本最低,管理最简单。只需要维护一个数据库实例,并且可以方便地进行数据库的扩展和维护。
- 缺点是数据隔离性相对较弱。如果数据库出现问题,可能会影响到所有租户。同时,在进行数据查询和处理时,需要根据租户标识符进行过滤,可能会影响性能。
2、应用程序层面
- 租户识别:
-
- 在应用程序中,需要能够识别当前请求来自哪个租户。可以通过多种方式实现,如在请求中包含租户标识符、使用会话信息、基于用户登录信息等。
- 一旦确定了租户,应用程序就可以根据租户的需求进行相应的处理,如访问特定的数据库、加载特定的配置等。
- 数据过滤:
-
- 在进行数据查询和处理时,应用程序需要根据租户标识符对数据进行过滤,确保只返回当前租户的数据。
- 可以在数据库查询语句中添加租户标识符作为过滤条件,或者在应用程序代码中进行数据过滤。
- 配置管理:
-
- 不同的租户可能有不同的配置需求,如界面显示、业务规则等。应用程序需要能够根据租户的配置进行相应的调整。
- 可以使用配置文件、数据库表或者其他方式来存储租户的配置信息,并在应用程序启动时加载相应的配置。
3、安全考虑
- 数据隔离:
-
- 确保不同租户的数据不能被其他租户访问。可以通过数据库层面的隔离机制和应用程序层面的数据过滤来实现。
- 对于敏感数据,还可以采用加密等方式来进一步提高安全性。
- 访问控制:
-
- 对租户的访问进行严格的控制,确保只有授权的用户才能访问相应的租户数据。
- 可以使用身份验证和授权机制,如用户登录、角色权限管理等。
- 审计和监控:
-
- 对租户的操作进行审计和监控,以便及时发现和处理安全问题。
- 可以记录租户的操作日志,定期进行安全审计,确保系统的安全性和合规性。
五、lambadaquery构建表达式
在 Java 中,LambdaQuery 通常用于构建数据库查询的表达式,它提供了一种简洁、类型安全的方式来构建复杂的查询条件。以下是关于使用 LambdaQuery 构建表达式的介绍:
1、基本用法
- 引入依赖:
-
- 首先,确保你的项目中引入了相应的数据库操作框架,如 MyBatis-Plus。
- 创建查询对象:
-
- 通过数据库操作对象(如 BaseMapper)获取 LambdaQuery 对象。例如:
LambdaQuery<User> query = userMapper.lambdaQuery();
- 构建查询条件:
-
- 使用 Lambda 表达式来指定查询条件。例如,查询年龄大于 18 岁的用户:
query.gt(User::getAge, 18);
- 这里,User::getAge是一个方法引用,表示获取 User 对象的 age 属性,gt方法表示大于条件。
2、常见的查询操作
- 相等条件:
-
- 使用eq方法表示相等条件。例如,查询用户名等于 “张三” 的用户:
query.eq(User::getUsername, "张三");
- 范围查询:
-
- 使用between方法进行范围查询。例如,查询年龄在 20 到 30 岁之间的用户:
query.between(User::getAge, 20, 30);
- 模糊查询:
-
- 使用like方法进行模糊查询。例如,查询用户名中包含 “张” 字的用户:
query.like(User::getUsername, "张");
- 排序:
-
- 使用orderByAsc和orderByDesc方法进行升序和降序排序。例如,按照年龄升序排序:
query.orderByAsc(User::getAge);
3、复杂查询条件组合
- 逻辑与(and):
-
- 可以连续调用多个条件方法来实现逻辑与。例如,查询年龄大于 18 岁且用户名等于 “张三” 的用户:
query.gt(User::getAge, 18).eq(User::getUsername, "张三");
- 逻辑或(or):
-
- 使用or方法实现逻辑或。例如,查询年龄大于 18 岁或者用户名等于 “张三” 的用户:
query.gt(User::getAge, 18).or().eq(User::getUsername, "张三");
4、分页查询
- 设置分页参数:
-
- 使用page方法设置分页参数。例如,查询第一页,每页显示 10 条数据:
Page<User> page = new Page<>(1, 10); query.page(page);
- 获取分页结果:
-
- 执行查询后,返回的是一个包含分页结果的 Page 对象。可以从 Page 对象中获取查询结果和分页信息。例如:
Page<User> resultPage = query.page(page); List<User> userList = resultPage.getRecords(); long total = resultPage.getTotal();
总之,LambdaQuery 提供了一种强大而灵活的方式来构建数据库查询表达式,可以根据具体的业务需求组合各种查询条件,实现复杂的数据库查询操作。
六、$和#的区别
在 MyBatis 中,
$和#都用于在 SQL 语句中引用参数,但它们有以下区别:
1、语法和使用场景
- #的使用:
-
- 通常用于引用参数的值,并将其安全地插入到 SQL 语句中。MyBatis 会对使用#引用的参数进行预处理,以防止 SQL 注入攻击。
- 例如:
SELECT * FROM user WHERE id = #{id}
- 在这个例子中,#{id}会被 MyBatis 替换为实际的参数值,并且会进行适当的转义处理,确保参数值不会破坏 SQL 语句的结构或导致安全问题。
- $的使用:
-
- 用于直接将参数值插入到 SQL 语句中,不会进行任何预处理。
- 例如:
SELECT * FROM user WHERE id = ${id}
- 在这个例子中,${id}会被直接替换为参数值,不会进行转义处理。如果参数值来自不可信的来源,可能会导致 SQL 注入攻击。
2、安全性
- #的安全性:
-
- 由于 MyBatis 会对使用#引用的参数进行预处理,所以它可以有效地防止 SQL 注入攻击。这使得#在大多数情况下是更安全的选择。
- 例如,如果用户输入的参数值包含 SQL 关键字或特殊字符,MyBatis 会对其进行转义,确保 SQL 语句的安全性。
- $的安全性:
-
- 使用$引用参数值时,不会进行任何预处理,因此如果参数值来自不可信的来源,可能会导致 SQL 注入攻击。
- 例如,如果用户输入的参数值包含 SQL 关键字或特殊字符,并且直接使用$插入到 SQL 语句中,可能会导致 SQL 语句被恶意篡改,从而引发安全问题。
3、灵活性
- #的灵活性:
-
- 在某些情况下,#的灵活性可能会受到一些限制。例如,在使用数据库函数或表名、列名等标识符时,不能直接使用#引用参数值。
- 例如,如果要根据动态的表名进行查询,不能使用#{tableName},而需要使用其他方式来实现动态 SQL。
- $的灵活性:
-
- $在某些情况下可以提供更高的灵活性。例如,可以直接使用$引用参数值来构建动态的表名、列名或 SQL 函数参数。
- 例如:
SELECT * FROM ${tableName} WHERE id = ${id}
- 在这个例子中,可以通过动态设置tableName和id参数值来实现对不同表的查询。
4、性能
- #的性能:
-
- 由于 MyBatis 会对使用#引用的参数进行预处理,可能会在一定程度上影响性能。特别是在处理大量参数或复杂的 SQL 语句时,预处理可能会增加一些开销。
- 然而,在大多数情况下,这种性能影响是可以忽略不计的,并且为了保证安全性,这种开销是值得的。
- $的性能:
-
- 使用$引用参数值时,不会进行预处理,因此在性能上可能会稍微优于#。但是,由于存在安全风险,不建议在需要保证安全性的情况下使用$。
七、如何实现一对多、多对一的查询
在数据库设计和查询中,一对多和多对一关系是常见的关联关系。以下分别介绍如何在 MyBatis 中实现一对多和多对一的查询。
1、一对多查询
假设我们有两个实体类,
Parent(父类)和
Child(子类),一个父类可以有多个子类。
- 数据库表设计:
-
- 创建两个表,分别对应Parent和Child实体。例如,可以有parent_table和child_table。
- 在child_table中设置一个外键字段,关联到parent_table的主键。
- 实体类设计:
-
- Parent实体类中包含一个List类型的属性,用于存储关联的子对象。
- Child实体类中包含一个Parent类型的属性,用于存储关联的父对象。
- MyBatis 映射文件配置:
-
- 在Parent的映射文件中,使用标签来配置一对多关系的查询。
- 例如:
<resultMap id="parentResultMap" type="Parent"> <id property="id" column="parent_id"/> <result property="name" column="parent_name"/> <collection property="children" ofType="Child" select="selectChildrenByParentId"/> </resultMap> <select id="selectParentById" resultMap="parentResultMap"> SELECT * FROM parent_table WHERE id = #{id} </select> <select id="selectChildrenByParentId" resultType="Child"> SELECT * FROM child_table WHERE parent_id = #{parentId} </select>
- 在这个例子中,首先查询父对象,然后通过标签中的select属性指定一个子查询来获取关联的子对象。
- 在代码中调用:
-
- 使用 MyBatis 的SqlSession或Mapper接口来调用查询方法。
- 例如:
SqlSession sqlSession = sqlSessionFactory.openSession(); ParentMapper parentMapper = sqlSession.getMapper(ParentMapper.class); Parent parent = parentMapper.selectParentById(parentId); sqlSession.close();
2、多对一查询
假设我们有两个实体类,
Student(学生)和
School(学校),多个学生属于一个学校。
- 数据库表设计:
-
- 创建两个表,分别对应Student和School实体。例如,可以有student_table和school_table。
- 在student_table中设置一个外键字段,关联到school_table的主键。
- 实体类设计:
-
- Student实体类中包含一个School类型的属性,用于存储关联的学校对象。
- School实体类中可以不包含对学生的直接引用。
- MyBatis 映射文件配置:
-
- 在Student的映射文件中,使用标签来配置多对一关系的查询。
- 例如:
<resultMap id="studentResultMap" type="Student"> <id property="id" column="student_id"/> <result property="name" column="student_name"/> <association property="school" javaType="School" select="selectSchoolById"/> </resultMap> <select id="selectStudentById" resultMap="studentResultMap"> SELECT * FROM student_table WHERE id = #{id} </select> <select id="selectSchoolById" resultType="School"> SELECT * FROM school_table WHERE id = #{schoolId} </select>
- 在这个例子中,首先查询学生对象,然后通过标签中的select属性指定一个子查询来获取关联的学校对象。
- 在代码中调用:
-
- 使用 MyBatis 的SqlSession或Mapper接口来调用查询方法。
- 例如:
SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student = studentMapper.selectStudentById(studentId); sqlSession.close();
八、怎么设置一级二级缓存
在 MyBatis 中,一级缓存和二级缓存可以提高数据查询的性能。以下是关于如何设置一级和二级缓存的方法:
1、一级缓存
- 概述:
-
- MyBatis 的一级缓存是 SqlSession 级别的缓存,也称为本地缓存。它在同一个 SqlSession 中对相同的查询语句和参数进行缓存,避免重复执行数据库查询。
- 设置方式:
-
- 一级缓存是默认开启的,无需进行特殊的配置。只要在同一个 SqlSession 中执行相同的查询语句,MyBatis 会自动使用一级缓存。
- 注意事项:
-
- 当 SqlSession 执行了任何 DML(数据操作语言,如插入、更新、删除)操作时,会清空一级缓存,以确保数据的一致性。
- 不同的 SqlSession 之间的一级缓存是相互独立的。
2、二级缓存
- 概述:
-
- MyBatis 的二级缓存是 Mapper 级别的缓存,可以在多个 SqlSession 之间共享。它可以缓存不同 SqlSession 对同一个 Mapper 中的查询结果,提高查询性能。
- 设置方式:
-
- 在 MyBatis 的配置文件(通常是mybatis-config.xml)中,需要开启二级缓存:
<configuration> <!-- 开启二级缓存 --> <settings> <setting name="cacheEnabled" value="true"/> </settings> <!-- 其他配置 --> </configuration>
- 在 Mapper XML 文件中,对需要使用二级缓存的 Mapper 进行配置:
<mapper namespace="com.example.mapper.UserMapper"> <!-- 开启当前 Mapper 的二级缓存 --> <cache/> <!-- SQL 查询语句和其他配置 --> </mapper>
- 实体类需要实现Serializable接口,以便在缓存中存储和读取:
public class User implements Serializable { // 实体类的属性和方法 }
- 注意事项:
-
- 二级缓存的使用需要谨慎,因为它可能会导致数据不一致的问题。如果在多个 SqlSession 中同时对同一个数据进行修改,二级缓存中的数据可能会过时。
- 可以通过设置缓存的刷新间隔、大小等参数来优化二级缓存的性能。例如:
<cache eviction="LRU" flushInterval="60000" size="1024" readOnly="true"/>
- 这里,eviction表示缓存的清除策略(如 LRU 最近最少使用),flushInterval表示缓存的刷新间隔(单位为毫秒),size表示缓存的大小,readOnly表示缓存是否只读。
九、如何封装返回结果
在使用 MyBatis 或类似框架进行开发时,封装返回结果可以提高代码的可维护性和可读性。以下是一些常见的方法来封装返回结果:
1、自定义响应类
- 创建响应类:
-
- 定义一个专门的响应类,用于封装查询结果或操作结果。这个类可以包含状态码、消息、数据等字段。
- 例如:
public class Result<T> { private int code; private String message; private T data; // 构造函数、getter 和 setter 方法 }
- 返回封装后的结果:
-
- 在业务逻辑方法中,根据操作结果设置响应类的状态码、消息和数据,并返回这个封装后的结果。
- 例如:
public Result<User> getUserById(int id) { try { User user = userMapper.getUserById(id); return new Result<>(200, "查询成功", user); } catch (Exception e) { return new Result<>(500, "查询失败:" + e.getMessage(), null); } }
2、使用通用工具类
- 创建工具类:
-
- 可以创建一个通用的工具类,用于封装返回结果。这个工具类可以提供一些静态方法,方便在不同的地方调用。
- 例如:
public class ResultUtil { public static <T> Result<T> success(T data) { return new Result<>(200, "操作成功", data); } public static <T> Result<T> error(int code, String message) { return new Result<>(code, message, null); } }
- 返回封装后的结果:
-
- 在业务逻辑方法中,使用工具类的方法来返回封装后的结果。
- 例如:
public Result<User> getUserById(int id) { try { User user = userMapper.getUserById(id); return ResultUtil.success(user); } catch (Exception e) { return ResultUtil.error(500, "查询失败:" + e.getMessage()); } }
3、结合异常处理
- 定义自定义异常:
-
- 可以定义一些自定义的异常类,用于表示特定的业务错误或异常情况。
- 例如:
public class BusinessException extends RuntimeException { private int code; private String message; // 构造函数、getter 和 setter 方法 }
- 在业务逻辑中抛出异常:
-
- 当出现业务错误或异常情况时,抛出自定义异常,并设置相应的状态码和消息。
- 例如:
public Result<User> getUserById(int id) { try { User user = userMapper.getUserById(id); return new Result<>(200, "查询成功", user); } catch (BusinessException e) { return new Result<>(e.getCode(), e.getMessage(), null); } catch (Exception e) { return new Result<>(500, "查询失败:" + e.getMessage(), null); } }
- 全局异常处理:
-
- 可以使用全局异常处理机制,捕获自定义异常和其他未处理的异常,并返回相应的封装结果。
- 例如,在 Spring Boot 中,可以使用@ControllerAdvice注解来定义全局异常处理类:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result handleBusinessException(BusinessException e) { return new Result<>(e.getCode(), e.getMessage(), null); } @ExceptionHandler(Exception.class) public Result handleException(Exception e) { return new Result<>(500, "系统错误:" + e.getMessage(), null); } }
十、如何返回自增主键
在 MyBatis 中,可以通过以下几种方式返回自增主键:
一、使用useGeneratedKeys和keyProperty属性
- 在 MyBatis 的 Mapper XML 文件中,对于插入语句,可以使用useGeneratedKeys="true"和keyProperty属性来指示 MyBatis 返回自增主键。
-
- 例如:
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert>
- 在这个例子中,useGeneratedKeys="true"表示使用数据库生成的主键,keyProperty="id"指定将自增主键赋值给实体类中的id属性。
- 在 Java 代码中调用插入方法后,可以直接从插入的对象中获取自增主键的值。
-
- 例如:
User user = new User(); user.setName("张三"); user.setAge(20); userMapper.insertUser(user); int id = user.getId(); // 获取自增主键的值
二、使用selectKey标签
- 在 Mapper XML 文件中,对于插入语句,可以使用标签来获取自增主键的值。
-
- 例如:
<insert id="insertUser"> <selectKey keyProperty="id" resultType="int" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert>
- 在这个例子中,标签的keyProperty属性指定将自增主键赋值给实体类中的id属性,resultType属性指定返回值的类型,order="AFTER"表示在插入语句执行后获取自增主键的值。SELECT LAST_INSERT_ID()是 MySQL 数据库中获取最后插入的自增主键值的方法,不同的数据库可能有不同的获取自增主键值的方法。
- 在 Java 代码中调用插入方法后,可以直接从插入的对象中获取自增主键的值。
-
- 例如:
User user = new User(); user.setName("张三"); user.setAge(20); userMapper.insertUser(user); int id = user.getId(); // 获取自增主键的值