返回随笔Java全栈
完成ENGINEERING NOTE

Mybatis

从实体类、Mapper、注解 SQL 和配置文件开始,记录 Mybatis 查询与项目集成的入门过程。

后端Mybatis

入门

入门程序

目的:使用Mybatis查询表中所有分类信息。

  • 第一步:构建一个mybaits项目,一个Category实体类(用以储存查询到的信息),数据库表category 在构建Category实体类时,需要有一个全参构造器和所有get()set()函数,字段列表类型,名称需要与数据库的字段列表一致。其中tinyint使用Short代替,varchar使用String代替。
public class Category {
    private Integer id;
    private String name;
    private Short type;
    private Short sort;
    private Short status;

    public Category(Integer id, String name, Short type, Short sort, Short status) {
        this.id = id;
        this.name = name;
        this.type = type;
        this.sort = sort;
        this.status = status;
    }

    get()与set()函数
    toString()函数
}
  • 引入mybatis相关依赖,并配置mybatis 打开application.properties文件,并在其中填入如下的语句:
# 配置数据库的链接信息
# 驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
# 用户名
spring.datasource.username=root
# 密码
spring.datasource.password=1234
  • 使用注解(或xml)编写sql语句 声明一个CategoryMapper接口,在其前面加上@Mapper注解。这个注解会自动将其封装成实体类(可以使用其类对象)。
@Mapper
public interface CategoryMapper {

    //Select注解表示需要执行的sql语句
    @Select("select * from category")
    public List<Category> list();  //使用List来接受多行数据
}

在test文件中进行测试。使用@Autowired来自动将相关依赖注入接口类对象。

 @Autowired
    private CategoryMapper categoryMapper;

 @Test
    void contextLoads() {
        List<Category> categoryList = categoryMapper.list();
        categoryList.stream().forEach(category -> {
            System.out.println(category);
        });
    }

配置SQL提示

@Select注解中的字符串并没有自动联想与自动纠错。选择@Select中的字符串,找到Show Context Actions->Inject language reference->MySql即可

JDBC

JDBC是sun公司提供的一套操作所有关系型数据库的规范,即接口。各个数据库厂商自己实现接口,提供数据库驱动jar包。使用JDBC编程时,执行的代码是jar包中的实现类。

Lombok

Lombok是一个实用的java类库,能通过注解的形式自动生成构造器等函数。

**注解** **作用**
@Getter/@Setter 添加get/set方法
@ToString 生成易阅读的tostring方法
@EqualsAndHashCode 重写equals方法和hashCode方法
@Data 提供了更综合的生成代码功能(涵盖以上)
@NoArgsConstructor 无参构造
@AllArgsConstructor 全参构造
引入依赖: ```plain text org.projectlombok lombok ``` ## **基础操作** ### **删除** 使用@Delete来指定SQL语句与函数。 函数的返回值可以用来记录操作影响的数据数量,使用int接受。#\{\}是一个占位符,用来捕获传递给函数的参数,并在语句中使用其参数。 如果接口方法形参只有一个普通类型的参数,#\{\}里的属性名可以随便写。 ```plain text @Mapper public interface MenuMapper {

   //根据ID删除数据    @Delete(“delete from mybatis.drinks where id = #{id}”)    //#{}是一个占位符    public int delete(Integer deleteId);   //返回值代表操作的记录数 }

### **预编译SQL**
默认情况下,mybatis日志不会输出,使用
```plain text
# 打开mybatis的日志,并输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

来将日志输出到控制台。

  • 预编译SQL 当使用#{}来指定参数时,会出现预编译SQL,#{}被解释为?,例如:
==>  Preparing: delete from mybatis.drinks where id = ?
==> Parameters: 22(Integer)

使用预编译SQL性能更高,且更加安全(防止SQL注入)。

  • SQL注入 SQL注入是通过操作输入的数据来修改事先定义好的SQL语句,达到执行代码对服务器进行攻击的操作。 例如,登录的SQL写法:
select count(*) from emp where username = 'username' and password = '1234'

而当输入的密码为’or’1’ = ’1时,SQL语句将变为:

select from(*) from emp where username = 'username' and password = ''or'1' = '1'

判断语句永远为真。这样就达到了攻击服务器的目的。 而使用预编译SQL即可预防这种情况。当然也可以使用${}使用拼接SQL语句,使用场景很少。

新增

使用@Insert注解,具体实现如下:

@Insert("insert into mybatis.drinks(id, name, category_id, price, img, description, status, update_time)" +
            " values (#{id}, #{name}, #{categoryId}, #{price}, #{img}, #{description}, #{status}, #{updateTime}) ")
    public void insert(Drinks drinks);

可以将传递的参数封装在一个对象中,且作为参数传递。注意:#{}中的参数需要与对象中一致,即遵守驼峰命名法。

主键返回

将封装的实体类传递给insert函数后,实体类会被清空,因此无法拿到当初填入的主键。可以使用@Options来进行一些设置,其中,keyProperty表示返回的主键 useGeneratedKeys = true表示将返回的主键值封装在字段中。

@Options(keyProperty = "id", useGeneratedKeys = true)
//使用@Options注解配置主键信息,useGeneratedKeys = true表示将返回的主键值封装在字段中
@Insert("insert into mybatis.drinks(id, name, category_id, price, img, description, status, update_time)" +
        " values (#{id}, #{name}, #{categoryId}, #{price}, #{img}, #{description}, #{status}, #{updateTime}) ")
public void insert2(Drinks drinks);

更新

@Update("update mybatis.user set id = #{id}, username = #{username}, password = #{password}")
public void update(Users users);

查询

@Select("select * from mybatis.drinks where id = #{id}")
public Drinks getById(Integer id);

查询方法返回值可以是一个集合(用来储存多个返回内容),可以是一个对象(返回单个元素),int(使用了count()函数)。 如果实体属性名与数据库表查询返回的字段名一致,mybatis会自动封装;如果不一致,则不会自动封装。解决方法如下:

  • 方案一:起别名
@Select("select id, name, category_id categoryId, price, img, description, status, update_time updateTime from mybatis.drinks where id = #{id}")
public Drinks getById(Integer id);
  • 方案二:使用@Results和@Result注解自动映射
@Results({
            @Result(column = "category_id", property = "categoryId"),
            @Result(column = "update_time", property = "updateTime"),
})
@Select("select * from mybatis.drinks where id = #{id}")
public Drinks getById(Integer id);
  • 方案三:使用mybatis驼峰命名自动映射 在application.properties中配置mybatis.configuration.map-underscore-to-camel-case=true即可。使用这种方法需要严格遵守:数据库中字段使用下划线命名,mybatis中使用驼峰命名法。

条件查询

目的:对name进行模糊查询,查询指定价格范围内的内容并以倒序排序:

@Select("select * from mybatis.drinks where name like concat('%', #{name}, '%') and status = #{status} and price between #{price1} and #{price2} order by category_id desc")
public List<Drinks> getBy(String name, Integer status, Double price1, Double price2);

需要注意的是,name后不是等于,是like;而且不能使用预编译SQL,需要使用字符串拼接函数concat()