目录
【K】mysql如何实现插入一条数据后立刻获取该数据自增长id进行调用
//1. 基本查询
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserById(int id);
//2. 动态查询
@Select("<script>" +
"SELECT * FROM users" +
"<if test='id != null'>" +
" WHERE id = #{id}" +
"</if>" +
"</script>")
List<User> selectUsersById(@Param("id") Integer id);
//3. 返回 Map
@Select("SELECT * FROM users")
@MapKey("id")
Map<Integer, User> selectUsersAsMap();
//4. 使用注解参数
@Select("SELECT * FROM ${tableName}")
List<User> selectUsersFromTable(@Param("tableName") String tableName);
//5. 结果映射
@Select("SELECT id, name, email FROM users WHERE id = #{id}")
@Results({
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "email", column = "email")
})
User selectUserDetailedById(int id);
//6. 联合查询
@Select("SELECT u.id, u.name, a.city FROM users u INNER JOIN address a ON u.id = a.user_id WHERE u.id = #{id}")
User selectUserWithAddress(int id);
//7. 分页查询
@Select("SELECT * FROM users LIMIT #{limit} OFFSET #{offset}")
List<User> selectUsersByPage(@Param("limit") int limit, @Param("offset") int offset);
在 MyBatis 中,Java 类型和 MySQL 数据库类型之间的映射是通过 `typeHandlers` 来实现的。以下是一些常见的 MySQL 数据类型与 Java 类型的对应关系,以及在 MyBatis 的 XML 配置文件中如何配置它们的示例。
MySQL 数据类型 | Java 类型 | MyBatis XML 配置中的类型别名 |
---|---|---|
CHAR | String | VARCHAR |
VARCHAR | String | VARCHAR |
BLOB | byte[] | BLOB |
TEXT | String | VARCHAR |
INTEGER | int /Integer | INTEGER |
TINYINT | byte /Byte | TINYINT |
SMALLINT | short /Short | SMALLINT |
MEDIUMINT | int /Integer | INTEGER |
BIGINT | long /Long | BIGINT |
FLOAT | float /Float | FLOAT |
DOUBLE | double /Double | DOUBLE |
DECIMAL | java.math.BigDecimal | DECIMAL |
DATE | java.sql.Date | DATE |
TIME | java.sql.Time | TIME |
DATETIME | java.sql.Timestamp | TIMESTAMP |
TIMESTAMP | java.sql.Timestamp | TIMESTAMP |
BOOLEAN | boolean /Boolean | BOOLEAN |
ENUM | String | VARCHAR |
SET | String | VARCHAR |
在 MyBatis 的配置文件 `mybatis-config.xml` 中,你不需要为每种类型映射都显式定义 `typeHandler`,因为 MyBatis 已经内置了对这些常见类型的处理。但如果你需要自定义类型处理器,你可以这样配置:
<typeHandlers>
<typeHandler handler="com.example.MyCustomTypeHandler"/>
</typeHandlers>
以下是一个包含所有类型映射的 MyBatis 映射文件 (`UserMapper.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 namespace="com.example.mapper.UserMapper">
<resultMap id="UserResultMap" type="User">
<result property="id" column="id" javaType="int" jdbcType="INTEGER" />
<result property="username" column="username" javaType="String" jdbcType="VARCHAR" />
<result property="password" column="password" javaType="String" jdbcType="VARCHAR" />
<result property="email" column="email" javaType="String" jdbcType="VARCHAR" />
<result property="profilePhoto" column="profile_photo" javaType="byte[]" jdbcType="BLOB" />
<result property="age" column="age" javaType="Integer" jdbcType="TINYINT" />
<result property="balance" column="balance" javaType="java.math.BigDecimal" jdbcType="DECIMAL" />
<result property="birthDate" column="birth_date" javaType="java.sql.Date" jdbcType="DATE" />
<result property="loginTime" column="login_time" javaType="java.sql.Timestamp" jdbcType="TIMESTAMP" />
<result property="isActive" column="is_active" javaType="Boolean" jdbcType="BOOLEAN" />
</resultMap>
<select id="selectUsers" resultMap="UserResultMap">
SELECT * FROM users
</select>
<!-- Other CRUD operations go here -->
</mapper>
在这个示例中,`<resultMap>` 定义了如何将数据库表 `users` 中的列映射到 Java 类 `User` 的属性上。每个 `<result>` 元素指定了一个属性到列的映射,包括 Java 类型 (`javaType`) 和 JDBC 类型 (`jdbcType`)。
请注意,实际开发中可能不需要为每个字段都显式指定 `jdbcType`,因为 MyBatis 通常能够推断出对应的类型。但在某些情况下,特别是当 MyBatis 无法准确推断类型时(例如对于 `null` 值),指定 `jdbcType` 可以避免类型相关的错误。
在 MyBatis 中,为了避免重复编写相同的 SQL 片段,可以使用 `<sql>` 元素定义可重用的 SQL 代码块,并通过 `<include>` 元素在需要的地方引入这些代码块。这种方式提高了代码的可维护性和可读性。以下是如何使用这些元素来提取和复用相同的代码段的总结:
?? 使用 `<sql>` 标签定义公共的 SQL 片段,并为其指定一个唯一的 `id`。这个 `id` 将在后续的 `<include>` 标签中被引用。
<sql id="common_sql_where">
<if test="id != null">
AND id = #{id}
</if>
<!-- 可以继续添加其他公共条件 -->
</sql>
?? 在实际的查询语句中,使用 `<include>` 标签引入公共 SQL 片段。通过 `refid` 属性指定需要引入的公共 SQL 片段的 `id`。
<select id="selectWithCommonWhere" resultType="YourResultType">
SELECT * FROM your_table WHERE 1=1
<include refid="common_sql_where"/>
</select>
?? 在公共 SQL 片段中,可以使用 MyBatis 的参数占位符 `#{}` 来引用传入的参数。确保传入的参数名称与 `<if>` 测试条件中的参数名称相匹配。
?? 可以将多个 `<sql>` 片段组合在一起,使用多个 `<include>` 标签来构建复杂的查询条件。
<select id="selectAdvanced" resultType="YourResultType">
SELECT * FROM your_table
WHERE 1=1
<include refid="common_sql_where"/>
<!-- 可以继续引入其他公共 SQL 片段 -->
</select>
?? - 确保 `<include>` 标签中的 `refid` 正确无误地指向了预定义的 `<sql>` 片段的 `id`。
?? - 在 `<sql>` 片段中定义的条件必须符合 SQL 语法,特别是在使用 `<if>` 条件时要注意不要遗漏必要的 SQL 关键字,比如 `AND`。
?? - 当在 `<sql>` 片段中使用动态 SQL(如 `<if>`)时,要注意条件的逻辑组合,以避免生成无效的 SQL。
通过这种方式,可以有效地管理和复用 SQL 代码,减少冗余,并使得 SQL 语句的维护变得更加简单。
useGeneratedKeys 取值范围true|false 默认值是:false。
含义:设置是否使用JDBC的.
getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中。
<insert id="insert" parameterType="com.hy.common.entity.User"
useGeneratedKeys="true" keyProperty="id">
insert into user (name, age)
values ( #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER})
</insert>
@Override
@Transactional(readOnly = false)
public int insertUser() {
User user = new User();
user.setName("why");
user.setAge(18);
int count = userMapper.insert(user);
System.out.println(count); // 插入的条数:1
int id = user.getId();
System.out.println(id); // 自增的id:666
return id;
}
先查询是否存在,存在就更新,不存在就添加.
如果数据量过大, 使用foreach批量操作
举例: 集合唯一索引? id
可以查询所有的id集合idSet,? 使用in idSet查询那些存在, 返回结果集oldSet.
然后遍历判断idSet是否存在oldSet, 存在走更新逻辑,不存在走添加逻辑.
REPLACE关键字的使用
????? 使用REPLACE的最大好处就是可以将DELETE和INSERT合二为一,形成一个原子操作。这样就可以不必考虑在同时使用DELETE和INSERT时添加事务等复杂操作了。
????? 在使用REPLACE时,表中必须有唯一索引,而且这个索引所在的字段不能允许空值,否则REPLACE就和INSERT完全一样的。
????? 在执行REPLACE后,系统返回了所影响的行数,如果返回1,说明没有重复的记录,如果返回2,说明有重复记录,系统先DELETE这条记录,然后再INSERT这条记录。
语法和INSERT非常的相似,如下面的REPLACE语句是插入或更新一条记录。
REPLACE INTO users (id,name,age) VALUES(1, '李嘉欣', 18);
驱动默认不开启支持多个sql执行。
在数据库连接的url增加参数allowMultiQueries,值为true,多个SQL直接一以“;”分隔即可
xxx.jdbcUrl=jdbc:mysql://xx.xx.xx:3306/xxxxx?characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true
&allowMultiQueries=true
<select id="getUserAndOrders" resultSets="user,orders">
select * from user where id = #{id};
select * from orders where user_id = #{id};
</select>
场景:
删除用户的时候需要先删除用户的外键关联数据,否则会触发规则报错。
本人遇到的是循环拼接不同字段条件的修改sql执行。
直接写多条语句,用“;”隔开即可
<delete id="deleteUserById" parameterType="String">
delete from sec_user_role where userId=#{id};
delete from sec_user where id=#{id};
</delete>
执行多个SQL查询并返回多个结果集的原因简述:
MyBatis执行多个查询并处理结果集的方法简述:
总结:
MyBatis支持一次执行多个SQL查询并返回多个结果集,这有助于优化性能和提升代码质量。
在MyBatis中,可以使用动态SQL功能来根据不同的条件拼接不同的SQL语句。动态SQL是MyBatis的一大特色,它允许在XML映射文件中构建灵活的SQL语句。主要通过<if>, <choose>, <when>, <otherwise>, <where>, <set>, <foreach>等元素来实现条件逻辑。
以下是一个使用MyBatis动态SQL的例子,我们将创建一个根据不同条件来查询用户的例子。
首先,定义一个User对象和一个UserMapper接口:
// User.java
public class User {
private Integer id;
private String name;
private String email;
// getters and setters...
}
// UserMapper.java
public interface UserMapper {
List<User> findUsers(Map<String, Object> params);
}
然后,在MyBatis的XML映射文件中,我们定义动态SQL查询:
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="findUsers" resultType="com.example.User">
SELECT id, name, email
FROM users
<where>
<if test="id != null">
AND id = #{id}
</if>
<if test="name != null">
AND name = #{name}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>
</mapper>
在这个例子中,<where>元素会智能地插入WHERE关键字,并且只有当内部的条件成立时才会插入相应的条件语句,并且会处理好条件语句前的AND或OR关键字。
现在,当我们调用findUsers方法时,可以传递一个包含id、name、email等键的Map对象。MyBatis会根据Map中的键值对是否存在来动态构建SQL语句。
// 使用MyBatis的SqlSession来执行查询
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> params = new HashMap<>();
params.put("name", "John Doe");
List<User> users = mapper.findUsers(params);
// 处理查询结果...
} finally {
sqlSession.close();
}
在上面的Java代码中,我们创建了一个包含"name"键的Map,并将其作为参数传递给findUsers方法。MyBatis会生成一个包含AND name = 'John Doe'条件的SQL语句,因为Map中只有"name"键。
不同条件拼接不同sql
</where>
ORDER BY
<if test="name != null and name !='' and name == '1'.toString()">
dept DESC
</if>
<if test="name != null and name !='' and name == '2'.toString()">
type DESC
</if>
Postman访问
注意事项
前言:
name代码为String,
XML 代码为: <if test="name != null and name !='' and name == '1'.toString()">
某些特殊业务场景下的, 为了替换#{demoWay}的替换写法...
<if test="demoWay != null and test="demoWay != '' ">
<if test="demoWay == 'equal' ">
<![CDATA[ and demo_str = #{demoStr} ]]> </if>
<if test="demoWay == 'gt' ">
<![CDATA[ and demo_str > #{demoStr} ]]> </if>
<if test="demoWay == 'lt' ">
<![CDATA[ and demo_str < #{demoStr} ]]> </if>
<if test="demoWay == 'gte' ">
<![CDATA[ and demo_str >= #{demoStr} ]]> </if>
<if test="demoWay == 'lte' ">
<![CDATA[ and demo_str <= #{demoStr} ]]> </if>
</if>
这种动态SQL的方法使得SQL语句的构建非常灵活,可以根据传入的参数不同构建不同的查询条件,而不必编写多个几乎相同的查询语句。这样的设计既简化了代码,也使得维护更加容易。
在数据库中,有时需要生成具有特定格式的唯一ID。例如,这里的ID由当前日期(格式为yyyymmdd)和一个顺序号(四位数)组成。每当需要添加新的记录时,就要生成一个新的ID,该ID比当天已有的最大ID大1。
原始的SQL语句试图获取当天最大的ID,然后将其加1以生成新的ID。如果当天没有ID,就默认使用'0001'
作为顺序号。
公司表获取最大ID且加1:
ID示例:yyyymmdd+0001
SELECT
IFNULL(RIGHT(ac.id + 1, 4), '0001') numMax
FROM
app_company ac
WHERE
LEFT(ac.id, 8) = DATE_FORMAT(now(), '%Y%m%d')
ORDER BY
RIGHT(ac.id, 4) DESC
LIMIT 1
这个查询有几个问题:
改进后的SQL语句使用MAX函数来找出当天最大的ID,然后通过IFNULL处理当天没有任何ID的情况。这样就不需要ORDER BY和LIMIT子句,因为MAX函数已经能够找到最大的ID。
SELECT
IFNULL(MAX(RIGHT(ac.id, 4)) + 1, '0001') numMax
FROM
app_company ac
WHERE
LEFT(ac.id, 8) = DATE_FORMAT(now(), '%Y%m%d')
在实际使用中,直接在数据库层面进行ID的生成可能会有并发问题,特别是在高并发的环境下。为了确保ID的唯一性和顺序,可能需要采用其他机制,比如应用层的锁或者使用数据库的序列(在MySQL 8.0+中为AUTO_INCREMENT属性)。
此外,如果ac.id是字符型字段,直接进行+ 1操作可能会导致错误,因为字符型和数值型的操作不同。在这种情况下,可能需要先将ID的顺序号部分转换为数值型,进行加法操作后再转换回字符型。
MyBatis 的 <where> 标签用于动态生成 SQL 查询语句中的 WHERE 条件。该标签的主要作用是简化 SQL 编写,并自动处理 WHERE 关键字和条件之间的逻辑关系(如 AND 或 OR)。当内部条件为空时,它会智能地省略 WHERE 关键字,而当有条件时,则会自动加上 WHERE 关键字。这样可以避免在动态 SQL 中手动编写复杂的逻辑来判断是否需要添加 WHERE 关键字。
<where> 标签的基本规则:
??? <where> 标签内部可以包含 <if>、<choose>、<when>、<otherwise> 等子标签。
??? 如果 <where> 标签内部的条件都不成立,则整个 <where> 标签不会生成任何内容。
??? 如果 <where> 标签内部至少有一个条件成立,则会自动在这些条件前添加 WHERE 关键字。
??? <where> 标签会自动处理条件之间的第一个 AND 或 OR 关键字。
示例:
假设我们有一个 User 表,包含 id、name、email 等字段,现在我们要根据这些字段的不同组合来查询用户。
UserMapper 接口:
public interface UserMapper {
List<User> findUsers(Map<String, Object> params);
}
UserMapper.xml 映射文件:
<mapper namespace="com.example.mapper.UserMapper">
<select id="findUsers" resultType="com.example.User">
SELECT id, name, email
FROM User
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select>
</mapper>
在上述例子中:
这种方式确保了 WHERE 条件的灵活性和正确性,同时避免了手动编写复杂的逻辑来控制 SQL 语句的准确性。使用 <where> 标签可以大大提高 SQL 语句的可维护性和可读性。
在 MyBatis 中,`resultMap` 是一个非常强大的映射工具,它能够处理复杂的数据库关系,如一对一、一对多和多对多的关系。通过正确使用 `resultMap`,可以在执行单个查询时获取关联的数据,而不必执行多次查询,这样可以提高应用程序的性能。
下面是一个使用 `resultMap` 来处理一对一和一对多关系的完整示例。
假设我们有以下三个表:
1. 用户表 `user`
2. 部门表 `department`
3. 用户联系人表 `user_linkman`
用户表(user):
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`age` int(2) unsigned DEFAULT NULL,
`department_id` int(11) NOT NULL COMMENT '部门id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
部门表(department):
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '部门id',
`department_name` varchar(255) DEFAULT NULL COMMENT '部门名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
用户联系人表(user_linkman):
CREATE TABLE `user_linkman` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户联系人表主键id',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`linkman_id` varchar(255) DEFAULT NULL COMMENT '联系人id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COMMENT='用户联系人表';
用户实体类(User):
package com.example.demo.entity;
import lombok.Data;
import java.util.List;
@Data
public class User {
private String id;
private String code;
private String name;
private int age;
private Department department; // 对应部门实体
private List<UserLinkman> linkmenList; // 对应联系人列表
}
在 MyBatis 的映射文件中,我们可以定义一个 `resultMap` 来描述如何从数据库结果集映射到我们的实体类。
<resultMap id="userResultMap" type="User">
<id property="id" column="id" />
<result property="code" column="code" />
<result property="name" column="name" />
<result property="age" column="age" />
<association property="department" column="department_id" javaType="Department" select="selectDepartmentById" />
<collection property="linkmenList" ofType="UserLinkman" column="id" select="selectLinkmanListByUserId" />
</resultMap>
<select id="selectDepartmentById" resultType="Department">
SELECT id, department_name AS 'departmentName' FROM department WHERE id = #{id}
</select>
<select id="selectLinkmanListByUserId" resultType="UserLinkman">
SELECT id, user_id AS 'userId', linkman_id AS 'linkmanId' FROM user_linkman WHERE user_id = #{id}
</select>
<select id="selectUserById" resultMap="userResultMap">
SELECT * FROM user WHERE id = #{id}
</select>
在上述配置中,我们定义了一个 `userResultMap`:
- `<association>` 用于处理一对一的关系,这里是用户和部门的关系。
- `<collection>` 用于处理一对多的关系,这里是用户和联系人的关系。
通过这种配置,当我们调用 `selectUserById` 查询时,MyBatis 会自动执行 `selectDepartmentById` 和 `selectLinkmanListByUserId` 查询来填充 `User` 实体中的 `department` 和 `linkmenList` 属性。
假设我们需要查询某次考试的所有单位信息,包括名称、参考人数、通过人数、通过率,以及本部门所有参考人员的信息。我们可以创建一个对应的 `resultMap`:
<resultMap id="CountResultMap" type="com.example.demo.entity.vo.ExamTestVo">
<result column="department" jdbcType="VARCHAR" property="department"/>
<result column="exam_num" jdbcType="VARCHAR" property="examNum"/>
<result column="pass_num" jdbcType="VARCHAR" property="passNum"/>
<result column="percent_pass" jdbcType="VARCHAR" property="percentPass"/>
<collection property="userList" javaType="list" column="id" ofType="User" select="selectUserListByExamId" />
</resultMap>
这里的 `<collection>` 标签将会把某个考试 ID 对应的所有用户信息作为一个列表映射到 `ExamTestVo` 的 `userList` 属性中。这样,通过单个查询,我们就能获取一个考试的所有相关信息,包括每个单位的详细数据和参考人员的列表。
MyBatis 提供了一系列内置的类型别名,用于简化 XML 配置文件中的类型引用。这些别名代表了 Java 中的一些常见类型。使用类型别名可以让你的 MyBatis 配置文件更加简洁易懂。
下面是一些常见的 MyBatis 内置类型别名及其对应的 Java 类型:
Java 类型 | MyBatis 别名 |
---|---|
byte | _byte |
long | _long |
short | _short |
int | _int |
Integer | _integer |
double | _double |
float | _float |
boolean | _boolean |
还有一些引用类型的别名:
Java 类型 | MyBatis 别名 |
---|---|
String | string |
Byte | byte |
Long | long |
Short | short |
Integer | int |
Double | double |
Float | float |
Boolean | boolean |
Date | date |
BigDecimal | decimal |
BigDecimal | bigdecimal |
Object | object |
Map | map |
HashMap | hashmap |
List | list |
ArrayList | arraylist |
Collection | collection |
Iterator | iterator |
使用这些类型别名的例子:
<select id="selectUserById" resultType="_int">
SELECT age FROM user WHERE id = #{id}
</select>
<select id="selectUserName" resultType="string">
SELECT name FROM user WHERE id = #{id}
</select>
<select id="selectAllUsers" resultType="map">
SELECT * FROM user
</select>
在上述例子中,`resultType="_int"` 表示查询的结果是一个基本类型 `int`,`resultType="string"` 表示查询的结果是 `String` 类型,而 `resultType="map"` 表示查询的结果是 `Map` 类型的集合。
要注意的是,当我们使用类型别名 `_int` 时,通常是在查询返回单个值的场景,比如统计数量或者查询某个特定的数值
有用请点赞,养成良好习惯!
疑问、交流、鼓励请留言!