当前位置 博文首页 > 俺叫啥好嘞的博客:SpringBoot之Mybatis

    俺叫啥好嘞的博客:SpringBoot之Mybatis

    作者:[db:作者] 时间:2021-09-14 16:23

    Mybatis使用详解和入门案例

    MyBatis 是一款标准的 ORM 框架,支持普通的 SQL 查询、存储过程和高级映射,它消除了几乎所有的 JDBC 代码和参数的手工设置,并实现对结果集的检索封装,让我们可以在程序中极简地操作数据库。

    对象关系映射(Object Relational Mapping)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

    1 MyBatis快速入门

    接下来,我们开始使用MyBatis体验一下吧。
    1.1 依赖引入

    SpringBoot 集成 MyBatis,需要导入 mybatis-spring-boot-starter 和 MySQL 的依赖,如下:

    mysql mysql-connector-java 8.0.18 runtime org.mybatis.spring.boot mybatis-spring-boot-starter 2.0.1

    1.2 配置文件

    spring:
    datasource: # 数据库配置
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: root
    password: 123456

    mybatis:

    实体类目录

    type-aliases-package: com.mutest.model
    configuration:
    map-underscore-to-camel-case: true # 驼峰命名规范
    mapper-locations: # 指向mapper文件目录
    - classpath:/mappers/.xml
    # - classpath:/mappers2/
    .xml 如果还有其他路径,追加即可

    数据库的相关配置,这里就不详细解说了。

    这里着重说明一下 mybatis的配置:
    type-aliases-package: 定义实体类目录
    mapper-locations:xml映射文件位置,如果xml文件放置在不同路径下,就按文中那种配置即可。
    map-underscore-to-camel-case: 用来开启驼峰命名规范,比较好用,比如数据库中字段名为 modify_time, 那么在实体类中定义名为 modifyTime 的属性,都会自动匹配到驼峰属性。如果不这样配置,当字段名和属性名不同时,就无法实现映射。

    启动类配置:

    SpringBoot 是如何知道哪个类是操作数据库的呢?

    一种方法是在 mapper 层对应的类上添加 @Mapper 注解即可,但这种方法有个弊端,当我们有很多个 mapper 时,每一个类上都得添加 @Mapper 注解。
    另一种比较简便的方法是在 Spring Boot 启动类上添加 @MaperScan 注解,用来扫描某个包下的所有 mapper。
    

    @SpringBootApplication
    @MapperScan(value = “com.mutest.dao”) // 要扫描的dao层路径
    public class MutestApplication {

    public static void main(String[] args) {
    	SpringApplication.run(MutestApplication.class, args);
    }
    

    }

    或者不使用@MapperScan 注解,而是直接在 Mapper 类上面添加注解 @Mapper,出于方便考虑,建议使用前者。
    2 基于注解使用MyBatis

    首先,准备好数据库数据:
    在这里插入图片描述
    以及实体类 Goods:

    @Data
    public class Goods {
    private Long id;
    private String name;
    private int type;
    private double price;
    private int size;
    private int status;
    private String description;
    private String modifyTime;
    private String createTime;
    }

    实体类的数据类型要和数据库字段一一对应,属性名和字段名尽量对应:

    Long 对应 bigint
    String 对应 varchar
    int 对应 int
    

    2.1 基础使用

    基于注解的方式比较简单,MyBatis 主要提供了 @Select 、@Insert、@Update、Delete 四个注解,分别对应查询、插入、更新、删除操作。我们看一下具体的例子。

    实现商品的增删改查:

    @Mapper // 如果启动类配置了Mapper扫描路径,这里可以不加该注解
    public interface GoodsDao {
    // 查询商品
    @Select(“SELECT * FROM mytest.goods”)
    List getGoodsList();

    // 新增商品
    @Insert("INSERT INTO mytest.goods(type,name,price,size,status,description)VALUES('0','电风扇',300,1000,1,'春风拂面')")
    int addGood();
    
    // 新增商品
    @Update("UPDATE mytest.goods SET type='0','劳力士手表',price=6000000,size=5,status=1,description='将光阴凝集于分秒间' WHERE id=1")
    void updateGood(JSONObject request);
    
    // 删除商品
    @Delete("DELETE FROM mytest.goods WHERE id=1")
    void deleteGood();
    

    }

    2.2 参数传递

    上面的例子演示了基本的增删改查语句,但在实际项目中,肯定不能将参数写成具体的值,这就涉及到如何传递参数的问题了。

    我们使用@Param标签来进行参数映射,同样是增删改查的例子:

    @Mapper
    public interface GoodsDao {
    // 多个参数时,@Param内名称和#{}内名称对应取值
    @Select(“SELECT * FROM mytest.goods where type = #{type} AND price > #{price}”)
    List getGoodsList(@Param(“type”) String type,@Param(“price”) double price);

    // 传入实体参数
    @Insert("INSERT INTO mytest.goods(type,name,price,size,status,description)VALUES(#{type},#{name},#{price},#{size},#{status},#{description})")
    int addGood(JSONObject request);
    
    @Update("UPDATE mytest.goods SET type=#{type},name=#{name},price=#{price},size=#{size},status=#{status},description=#{description} WHERE id=#{id}")
    void updateGood(JSONObject request);
    
    @Delete("DELETE FROM mytest.goods WHERE id=#{id}")
    void deleteGood(@Param("id") int id);
    

    在执行时,系统会自动读取对象的属性并值赋值到同名的 #{xxx} 中。

    值得注意的是,# 符号和$ 符号的区别

    @Select(“SELECT * FROM mytest.goods WHERE name = #{name}”)
    Goods getGoodsByName(@Param(“name”) String name);

    @Select(“SELECT * FROM mytest.goods WHERE name = ‘${name}’”)
    Goods getGoodsByName(@Param(“name”) String name);

    会对SQL进行预处理,而使用 $ 时会直接将值拼接到SQL中。使用 $ 有SQL注入的风险,但当库表名需要进行参数化时适合使用 $。

    2.3 字段映射

    实际项目中,经常出现Mysql命名规范与Java的差异性导致的数据库字段名与Java实体类变量名不一致的情况。这个在前文中,已经讲述了通过配置文件进行映射的解决方案,那么在注解版如何解决这个问题呢?

    Mybatis提供了两个注解:@Results 和 @Result 注解,这两个注解配合来使用,主要作用是将数据库中查询到的数值转化为具体的字段,修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类属性和数据库属性名保持一致,就不需要这个属性来修饰。示例如下:

    @Select(“SELECT * FROM mytest.goods”)
    @Results({
    @Result(property = “modifyTime”, column = “modify_time”),
    @Result(property = “createTime”, column = “create_time”)
    })
    List getGoodsList();

    3 基于XML使用MyBatis

    xml是MyBatis最开始支持的方式,它相比注解方式,能加工更复杂的SQL语句。下面我们来快速实现一下xml方式。
    3.1 基础使用

    首先,之前的yml配置文件中,有mybatis的相关配置:

    mybatis:

    实体类目录

    type-aliases-package: com.mutest.model
    configuration:
    map-underscore-to-camel-case: true # 驼峰命名规范
    mapper-locations: # 指向mapper文件目录
    - classpath:/mappers/.xml
    # - classpath:/mappers2/
    .xml 如果还有其他路径,追加即可

    一般,我们将Mapper文件集中放置在resources目录下,这跟配置文件的mapper-locations配置项是相对应的。

    第一步,当相应的xml文件建好后,写入固定的文件头:

    <?xml version="1.0" encoding="UTF-8" ?>

    第二步,指明xml文件对应的 Mapper 类(或者说dao类)地址:

    1
    

    第三步,配置表结构和实体类的对应关系:

    第四步,写SQL语句:

    SELECT * FROM mytest.goods UPDATE mytest.goods SET type=#{type}, name=#{name}, price=#{price}, size=#{size}, status=#{status}, description=#{description} WHERE id=#{id} INSERT INTO mytest.goods(type,name,price,size,status,description) VALUES (#{type},#{name},#{price},#{size},#{status},#{description}) DELETE FROM mytest.goods WHERE id =#{id}

    Dao 层的代码:

    @Mapper
    public interface GoodsDao {
    List getGoodsList();

    int addGood(JSONObject request);
    
    void updateGood(JSONObject request);
    
    void deleteGood(@Param("id") int id);
    

    }

    3.2 高级操作
    3.2.1 复用SQL

    MyBatis XML 有一个特点是可以复用 XML,比如我们公用的一些 XML 片段可以提取出来,在其他 SQL 中去引用。例如:

    id, type,name,price,size,status,description,create_time,modify_time SELECT FROM mytest.goods

    3.2.2 if标签实现参数校验

    当我们修改数据库数据时,可能对一行数据的部分字段进行修改,但具体修改哪些字段并不能事先确定,这时候就需要用到动态sql语句了,加入if标签进行一个非空判断:

    UPDATE users name= #{name}, type= #{type}, price= #{price}, size= #{size}, status= #{status}, description= #{description}, 1 = 1 WHERE id = #{id}

    3.2.3 foreach实现批量操作

    说到批量插入和更新,我们想到的最直接的方式就是循环操作,但这样的话,效率可想而知地低。为了高效地实现批量操作,MyBatis为我们提供了丰富的标签。下面我们用几个案例体验一下吧。

    首先,数据库配置中要将allowMultiQueries置为true,例如:

    jdbc:mysql://localhost:3306?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false

    1
    

    一、批量插入

    首先,看一下批量插入的xml样板写法:

    INSERT INTO mutest.student(id,name) VALUES (#{student.id},#{student.name}) collection对应的是入参集合,item对应集合中的一项,separator指定各项之间的分隔符

    当然也可以使用trim标签:

    INSERT INTO mutest.student(id,name) VALUES #{student.id}, #{student.name}

    然后,在dao层调用时,传入参数为学生实体的集合:

    int addStudentBatch(JSONArray studentList);

    1
    

    studentList的数据样板:

    [
    {
    “id”: 1,
    “name”: “爱因斯坦”
    },
    {
    “id”: 2,
    “name”: “法拉第”
    },
    {
    “id”: 1,
    “name”: “莱布尼兹”
    }
    ]

    二、批量更新

    批量更新的xml:

    UPDATE mutest.student name=#{item.name}, age=#{item.age} WHERE id=#{student.id};
    要注意的是set标签,会智能判断
    

    通过输出SQL发现,批量更新其实是将多个更新语句一起提交给了Mysql服务器:

    Preparing: UPDATE mutest.student SET name=?, age=? WHERE id=?; UPDATE mutest.student SET name=?, age=? WHERE id=?;
    Parameters: zhangsan2(String), 20(Integer), 1(Integer), lisi2(String), 21(Integer), 2(Integer)
    Updates: 1

    1
    2
    3
    

    3.3 总结
    JdbcType对应关系

    JDBCType JavaType
    CHAR String
    VARCHAR String
    LONGVARCHAR String
    NUMERIC java.math.BigDecimal
    DECIMAL java.math.BigDecimal
    BIT boolean
    BOOLEAN boolean
    TINYINT byte
    SMALLINT short
    INTEGER int
    BIGINT long
    REAL float
    FLOAT double
    DOUBLE double
    BINARY byte[]
    VARBINARY byte[]
    LONGVARBINARY byte[]
    DATE java.sql.Date
    TIME java.sql.Time
    TIMESTAMP java.sql.Timestamp
    CLOB Clob
    BLOB Blob
    ARRAY Array
    DISTINCT mapping of underlying type
    STRUCT Struct
    REF Ref
    DATALINK java.net.URL[color=red][/color]

    parameterType
    4 附件
    5.1 测试数据
    5.2 完整pom.xml
    5.3 完整mapper.xml

    cs