仍然以前面的User表为模型,实行增删改查操作

一、增

UserDao:

  1. void create(User user);

mapper:

  1. <insert id="create" parameterType="com.example.test.model.User">
  2. INSERT INTO user (
  3. username, password, nickname, phone, status, create_time, icon, gender, birthday
  4. )
  5. VALUES
  6. (
  7. #{username}, #{password}, #{nickname}, #{phone}, #{status}, now(), #{icon}, #{gender}, #{birthday}
  8. );
  9. </insert>

增加数据使用 insert 标签

insert 的几个属性说明:

  • id 唯一标识,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
  • parameterType 参数的类型,使用动态代理之后和方法的参数类型一致
  • useGeneratedKeys 开启主键回写
  • keyColumn 指定数据库的主键
  • keyProperty 主键对应的pojo属性名
  • 标签内部 具体的sql语句

UserServiceImpl:

@Override
public void create(User user) {
    userDao.create(user);
}

UserController:

@PostMapping("/create")
public void create(User user) {
    userService.create(user);
}

请求测试:
002.png

@Insert

如果使用注解,可以使用 @Insert 插入数据:

    @Insert("INSERT INTO user(user_name,pass_word,user_sex) VALUES(#{userName}, #{passWord}, #{userSex})")
    void create(User user);

二、删

UserDao:

void delete(Long id);

mapper:

<delete id="delete">
    DELETE FROM user where id=#{id}
</delete>

删除数据使用 delete 标签

delete 的几个属性说明:

  • id 唯一标识,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
  • parameterType 参数的类型,使用动态代理之后和方法的参数类型一致
  • 标签内部 具体的sql语句。

UserServiceImpl:

@Override
public void delete(Long id) {
    userDao.delete(id);
}

UserController:

@PostMapping("/delete/{id}")
public void delete(@PathVariable Long id) {
    userService.delete(id);
}

请求测试:http://localhost:10010/user/delete/21

@Delete

如果使用注解,可以使用 @Delete 删除数据:

    @Delete("DELETE FROM user WHERE id=#{id}")
    void delete(Long id);

三、改

UserDao:

void update(User user);

mapper:

<update id="update">
    UPDATE user
    <trim prefix="set" suffixOverrides=",">
        <if test="username!=null">username = #{username},</if>
        <if test="password!=null">password = #{password},</if>
        <if test="nickname!=null">nickname = #{nickname},</if>
        <if test="gender!=null">gender = #{gender},</if>
        create_time = now(),
    </trim>
    WHERE
    (id = #{id});
</update>

修改数据使用 update 标签,由于传入的字段数据是可选的,使用 trim 包裹,使用 if 判断后生成对应的sql

update 的几个属性说明:

  • id 唯一标识,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
  • parameterType 参数的类型,使用动态代理之后和方法的参数类型一致
  • 标签内部 具体的sql语句

UserServiceImpl:

@Override
public void update(User user) {
    userDao.update(user);
}

UserController:

@PostMapping("/update")
public void update(User user) {
    userService.update(user);
}

请求测试:http://localhost:8080/user/update?id=19&username=test&password=abc&nickname=t&gender=2

@Update

如果使用注解,可以使用 @Update 修改数据:

    @Update("UPDATE user SET user_name=#{userName},nick_name=#{nickName} WHERE id=#{id}")
    void update(User user);

四、查

查询单条数据

UserDao:

User query(Long id);

mapper:

<select id="query" resultType="com.example.test.model.User">
    SELECT * from user where id=#{id}
</select>

查询数据使用 select 标签,使用 resultType 指定返回数据类型

UserServiceImpl:

@Override
public User query(Long id) {
    return userDao.query(id);
}

UserController:

@GetMapping("/{id}")
public User query(@PathVariable Long id) {
    return userService.query(id);
}

请求测试:http://localhost:10010/user/19

查询多条数据

UserDao:

List<User> list();

mapper:

<select id="list" resultType="com.example.test.model.User">
    SELECT * from user
</select>

查询数据使用 select 标签,使用 resultType 指定返回数据类型

UserServiceImpl:

@Override
public List<User> list() {
    return userDao.list();
}

UserController:

@GetMapping("/list")
public List<User> list() {
    return userService.list();
}

请求测试:http://localhost:10010/user/list

@Select

可以使用 @Select 注解查询数据。示例:

public interface UserMapper {
    @Select("SELECT * FROM user")
    @Results({
            @Result(property = "userSex",  column = "user_sex", javaType = UserSexEnum.class),
            @Result(property = "nickName", column = "nick_name")
    })
    List<UserEntity> getAll();

    @Select("SELECT * FROM user WHERE id = #{id}")
    @Results({
            @Result(property = "userSex",  column = "user_sex", javaType = UserSexEnum.class),
            @Result(property = "nickName", column = "nick_name")
    })
    UserEntity getOne(Long id);

    @Select("SELECT count(*) FROM user")
    int count();
}

五、#$的区别

  1. #{} 预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
  2. ${} 普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数

示例:
DAO中:

User queryUserListByName2(@Param("username") String username2);

Mapper中:

<select id="queryUserListByName1" resultType="com.example.test.model.User">
    select * from tb_user WHERE user_name=#{username}
</select>

<select id="queryUserListByName2" resultType="com.example.test.model.User">
    select * from tb_user WHERE user_name='${username}'
</select>

注意到,使用$时需要手动添加''

六、参数传递

实体参数

如果传入的是一个实体,mapper将自动解开其内部参数的名称:

void insert(UserEntity user);

在mapper中解开:

    <insert id="insert" parameterType="UserEntity">
        INSERT INTO
        user
        (user_name, pass_word, user_sex)
        VALUES
        (#{userName}, #{passWord}, #{userSex})
    </insert>

非实体单参数

如果非实体,且只有一个参数,DAO中的参数会自动对应到mapper:

public List<User> queryUserByTableName(String tableName);
<select id="queryUserByTableName" resultType="com.example.test.model.User">
    select * from ${tableName}
</select>

非实体多参数

但是如果有多个参数,且在DAO中没有指定参数的名字:

public User login(String userName, String password);
<select id="login" resultType="com.example.test.model.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

这样就会报错:

org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]

解决方案一:使用位置参数

<select id="login" resultType="com.example.test.model.User">
    select * from tb_user where user_name = #{0} and password = #{1}
</select>

解决方案二:使用param参数

<select id="login" resultType="com.example.test.model.User">
    select * from tb_user where user_name = #{param1} and password = #{param2}
</select>

终极解决方案:在DAO中使用 @Param 注解

public User login(@Param("userName") String userName, @Param("password") String password);
<select id="login" resultType="com.example.test.model.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

七、枚举类型

如果实体中某个字段为枚举类型,比如性别:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String userName;
    private String passWord;
    private UserSexEnum userSex;
    private String nickName;

    public UserEntity(String userName, String passWord, UserSexEnum userSex) {
        super();
        this.passWord = passWord;
        this.userName = userName;
        this.userSex = userSex;
    }
}

性别的枚举如下:

public enum UserSexEnum {
    MAN, WOMAN
}

那么数据表中的字段应为超过枚举项字段最大长度的一个字符串:

CREATE TABLE if not exists `user` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_name` varchar(255) DEFAULT NULL,
    `pass_word` varchar(255) DEFAULT NULL,
    `user_sex` varchar(10) DEFAULT NULL,
    `nick_name` varchar(255) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

插入数据:

userMapper.insert(new UserEntity("xiaoyu", "a123456", UserSexEnum.MAN));
userMapper.insert(new UserEntity("qiaoer", "b123456", UserSexEnum.WOMAN));

插入后:
Snipaste_2021-01-06_11-36-33.webp

八、设置日期时间格式

在模型中配置 @JsonFormat 或者 @DateTimeFormat 注解,指定日期时间格式:

...

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;

@Data
public class User implements Serializable {
    ...

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}

这样,在查询或添加数据的时候,都可以使用指定的格式了。

时间格式的返回示例:

{
    "createTime": "2021-01-06 14:45:43",
    "updateTime": "2021-01-06T14:46:20.000+0000"
}