【Mybatis】Mybatis之使用注解实现增删改查

1,119 阅读5分钟

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

上文介绍了Mybatis之使用XML实现增删改查。在Mybatis中,虽然使用XML的方式较为普遍,但是其实Mybatis也是支持使用注解来进行增删改查的,本文就来介绍一下在Mybatis中基于注解实现增删改查的方法。

Mybatis所支持的注解都在org.apache.ibatis.annotations包中,本文只挑选常用的一些注解进行演示其使用。

image.png

注解位置与之对应的XML标签描述
@Arg-arg/idArg单参数构造方法,是 ConstructorArgs 集合的一部分。name属性用于表示构造方法中的参数名称。columnPrefix属性用于构造方法中参数也是一个POJO类时,将具有此属性值前缀的列通过声明的resultMap进行映射。
@AutomapConstructor构造方法-用于声明自动映射的构造方法
@CacheNamespacecache为给定的命名空间(比如类)配置缓存。
@CacheNamespaceRefcacheRef参照另外一个命名空间的缓存来使用。
@Case-case单独实例的值和它对应的映射。
@ConstructorArgs方法constructor收集一组结果传递给一个结果对象的构造方法。
@Delete方法delete代表将会被执行的 SQL 语句。
@DeleteProvider方法delete允许构建动态 SQL
@Flush方法-如果使用了这个注解,定义在 Mapper 接口中的方法能够调用 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上)
@Insert方法insert代表将会被执行的 SQL 语句。
@InsertProvider方法insert允许构建动态 SQL
@Lang方法lang属性配置解析SQL所需要的Driver
@Many-collection映射到复杂类型的集合属性。
@MapKey方法-这是一个用在返回值为 Map 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有: value,填入的是对象的属性名,作为 Map 的 key 值。
@Mapper类、方法、属性、参数-用于和spring进行整合。
@One-association复杂类型的单独属性值映射。
@Options方法-映射语句的属性
@Param参数-映射方法的形参。
@Property-property指定参数值或占位值。
@Result-result/id在列和属性或字段之间的单独结果映射。
@ResultMap方法-给 @Select 或者 @SelectProvider 提供在 XML 映射中的 的id。
@Results方法resultMap结果映射的列表
@ResultType方法-在使用了结果处理器的情况下使用。仅在方法返回类型是 void 的情况下生效。
@Select方法select代表将会被执行的 SQL 语句。
@SelectKey方法selectKey功能与 selectKey 标签完全一致
@SelectProvider方法select允许构建动态 SQL
@TypeDiscriminator方法discriminator一组实例值被用来决定结果映射的表现
@Update方法update代表将会被执行的 SQL 语句。
@UpdateProvider方法update允许构建动态 SQL

准备

// 商品分类POJO
public class Category {
    /**
     * ID
     */
    private Integer id;
    /**
     * 分类名称
     */
    private String name;
    //省略getter/setter等方法
}
// 商品POJO
public class Purchase {

    /**
     * ID
     */
    private Integer id;
    /**
     * 商品名称
     */
    private String name;
    /**
     * 商品价格
     */
    private Integer price;
    /**
     * 商品分类
     */
    private Integer category;
    
    public Purchase() {
    }

    public Purchase(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
    
    //省略getter/setter等方法
}

增加

Mapper中的方法

//Insert注解传入的参数是字符串数组,因此可以将SQL语句分解成字符串数组,也可以拼成一个字符串
// @Insert("insert into purchase (id, name, price, category) values(#{id},#{name},#{price},#{category})")
@Insert({"insert", "into purchase", " (id, name, price, category)", " values(#{id},#{name},#{price},#{category})"})
//Options注解用于插入数据后返回数据的自增ID
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
int insertAnnoPojo(Purchase purchase);

测试代码及结果,可以看到,刚插入的数据的id已经返回了。

@Test
public void insert() {
    PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
    //组装参数
    Purchase purchase = new Purchase();
    purchase.setName("西瓜");
    purchase.setPrice(12);
    purchase.setCategory(3);
    mapper.insertAnnoPojo(purchase);
    System.out.println(purchase);
}
DEBUG [main] - ==>  Preparing: insert into purchase (id, name, price, category) values(?,?,?,?) 
DEBUG [main] - ==> Parameters: null, 西瓜(String), 12(Integer), 3(Integer)
DEBUG [main] - <==    Updates: 1
Purchase{id=12, name='西瓜', price=12, category=3}

查询

Mapper接口中的方法

常用注解

//使用ConstructorArgs与Arg注解,通过构造方法为POJO赋值
@Select("select id,name from purchase where id = #{id}")
@ConstructorArgs({
        @Arg(column = "id", javaType = Integer.class, id = true),
        @Arg(column = "name", javaType = String.class)
})
Purchase findAnnoPojo(Purchase purchase);

//使用Results与Result注解,通过结果集映射的方式为POJO赋值,这种方式一般在列名与属性名不一致的情况下使用,这里是为了做演示。
//Results注解的id是为这个结果集起一个名字,其他的查询也可以通过这个名字使用到这个结果集
@Select("select id,name from purchase where id = #{id}")
@Results(id = "purchaseMap", value = {
        @Result(property = "id", column = "id", javaType = Integer.class, jdbcType = JdbcType.INTEGER, id = true),
        @Result(property = "name", column = "name", javaType = String.class, jdbcType = JdbcType.VARCHAR)
})
Purchase findAnnoPojo(Purchase purchase);

//使用ResultMap注解实现结果集复用
@Select("select id,name from purchase where id = #{id}")
@ResultMap("purchaseMap")
Purchase findAnnoById(Integer id);

ResultType注解用法

// 自定义结果处理器,实现org.apache.ibatis.session.ResultHandler接口
public class PurchaseResultHandler implements ResultHandler {

    List<Purchase> purchases;

    public PurchaseResultHandler() {
        super();
        this.purchases = new ArrayList<>();
    }

    //实现接口中的方法,并在这里处理查询返回的数据
    @Override
    public void handleResult(ResultContext resultContext) {
        Purchase purchase = (Purchase) resultContext.getResultObject();
        purchases.add(purchase);
    }

    //通过get方法获取结果集
    public List<Purchase> getPurchases() {
        return purchases;
    }
}

// Mapper中方法的写法
@Select("select id,name from purchase where id = #{id}")
@ResultType(Purchase.class)
void findAnnoById(PurchaseResultHandler resultHandler, @Param("id") Integer id);

// 测试代码
@Test
public void query() {
    PurchaseResultHandler resultHandler = new PurchaseResultHandler();
    mapper.findAnnoById(resultHandler, 12);
    System.out.println(resultHandler.getPurchases());
}

一对一查询

  • POJO类
public class PurchaseVO {

    /**
     * ID
     */
    private Integer id;
    /**
     * 名称
     */
    private String name;
    /**
     * 价格
     */
    private Integer price;
    /**
     * 分类
     */
    private Category category;
    //省略getter/setter等方法
}
  • Mapper接口中的方法,使用@One注解实现
@Select("select * from purchase where id = #{id}")
@Results(id = "purchaseVoMapper", value = {
        @Result(property = "category", column = "category", one = @One(select = "org.apache.ibatis.z_run.mapper.PurchaseMapper.findCategoryById"))
})
PurchaseVO findPurchaseById(Integer id);

@Select("select * from category where id = #{id}")
Category findCategoryById(Integer id);
  • 测试代码及查询结果
@Test
public void query() {
    PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
    System.out.println(mapper.findPurchaseById(11));
}
DEBUG [main] - ==>  Preparing: select * from purchase where id = ? 
DEBUG [main] - ==> Parameters: 11(Integer)
DEBUG [main] - ====>  Preparing: select * from category where id = ? 
DEBUG [main] - ====> Parameters: 1(Integer)
DEBUG [main] - <====      Total: 1
DEBUG [main] - <==      Total: 1
Purchase{id=11, name='火腿', price=3, category=Category{id=1, name='饮料'}}

一对多查询

  • POJO类
public class CategoryVO {
    private Integer id;
    private String name;
    private List<Purchase> purchases;
    //省略getter/setter等方法
}
  • Mapper接口中的方法
@Select("select * from category where id = #{id}")
// 这里必须要写id的映射关系,否则id值为null
@Results(id = "categoryVoMapper", value = {
        @Result(property = "id", column = "id"),
        @Result(property = "purchases", column = "id", many = @Many(select = "org.apache.ibatis.z_run.mapper.PurchaseMapper.findPurchaseByCategory"))
})
CategoryVO findCategoryByID(Integer id);

@Select("select * from purchase where category = #{category}")
Purchase findPurchaseByCategory(Integer category);
  • 测试代码及查询结果
@Test
public void query() {
    PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
    System.out.println(mapper.findCategoryByID(1));
}
DEBUG [main] - ==>  Preparing: select * from category where id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - ====>  Preparing: select * from purchase where category = ? 
DEBUG [main] - ====> Parameters: 1(Integer)
DEBUG [main] - <====      Total: 5
DEBUG [main] - <==      Total: 1
CategoryVO{id=1, name='饮料', purchases=[Purchase{id=1, name='可乐', price=3, category=1}, Purchase{id=8, name='火腿', price=3, category=1}, Purchase{id=9, name='火腿', price=3, category=1}, Purchase{id=10, name='火腿', price=3, category=1}, Purchase{id=11, name='火腿', price=3, category=1}]}

修改

// 通常用法,使用Update注解
@Update("update purchase set price = #{price} where id = #{id}")
int updateAnnoById(@Param("id") Integer id, @Param("price") Integer price);

// 使用UpdateProvider注解,使用动态构建的SQL
@UpdateProvider(type = SqlProvider.class, method = "provideUpdate")
int updateAnnoById(@Param("id") Integer id, @Param("price") Integer price);

class SqlProvider {
    public String provideUpdate(@Param("id") Integer id, @Param("price") Integer price) {
        StringBuilder sql = new StringBuilder();
        if (id != null) {
            sql.append("update purchase ");
            if (price != null) {
                sql.append("set price = #{price}");
            }
            sql.append(" where id = #{id}");
        }
        return sql.toString();
    }
}

删除

@Delete("delete from purchase where id = #{id}")
int deleteAnnoById(Integer id);

以上便是使用Mybatis注解完成增删改查的方法。由于在Mapper接口的方法上写SQL会显得可读性较差(特别是很复杂的SQL),因此这种方式了解即可,以后如果遇到有人使用这种方式写代码,自己能看懂就行。