MyBatis 注解方式就是将 SQL 语句直接写在DAO层的接口上。

在黑马录制的2018年双元视频课:\08 SSM整合案例【企业权限管理系统】\07.订单操作  有使用MyBatis注解进行多表关联查询的案例,在下文会有使用注解的补充说明。

这种方式的优点是 :对于需求比较简单的系统,效率较高。缺点是 ,当 SQL 有变化时都需要重新编译代码, 一般情况下不建议使用MyBatis的注解方式 。

因此,(原书)本章不会进行深入讲解。在MyBatis注解 SQL 中,最基本的就是@Select 、@Insert 、@Update 和@Delete 四种 。 下面以 RoleMapper 为例,对这几个注解的用法进行讲解 。

3.1 @Select 注解

在 cn.bjut.simple.mapper.RoleMapper 接口中添加如下注解方法。

public interface RoleMapper {
@Select({ "select id,role_name roleName, enabled, create_by createBy, create_time createTime ",
"from sys_role ",
"where id = #{id}" })
SysRole selectById(Long id);
}
使用注解方式同样需要考虑表字段和 Java 属性字段映射的问题,在第 2 章 中己经讲过 XML方式是如何实现宇段映射的,接下来看一下注解方式是如何实现的 。
第一种是通过 SQL 语句使用别名来实现,上面的例子中已经使用过 。 除此之外还有另外两种方式分别是:
  1. 使用 mapUnderscoreToCamelCase 配置方式自动映射。(springboot使用通用Mapper启动器,就是默认true)
  2. 以及使用 resultMap方式,手动指定映射下面详细说明 。

3.1.1 使用 mapUnderscoreToCamelCase 配置

在数据库中,由于大多数数据库设置不区分大小写 ,因此下画线方式的命名很常见,如 user_name 、 user_email 。在 Java 中, 一般都使用驼峰式命名,如 userName 、 userEmail 。
因为数据库和 Java 中的这两种命名方式很常见,因此 MyBatis 还提供 了 一个全局属性
mapUnderscoreToCamelCase ,通过配置这个属性为 true 可以自动将以下画线方式命名的
数据库列映射到 Java对象的驼峰式命名属性中。这个属性默认为 false ,如果想要使用该功能,
需要在 MyBatis 的配置文件(第 l 章中 的 mybatis-config.xml 文件)中增加如下配置。

    <settings>

        <!-- 其他mybatis配置 -->
<setting name=" mapUnderscoreToCamelCase" value="true"/>
</settings>

使用这种配置方式不需要手动指定别名 , MyBatis 字段按照 “下画线转驼峰”的方式 自动映射,@Select 注解中的 SQL 可以写成如下这种方式。

    @Select("select id,role_name, enabled, create_by, create_time from sys_role where id = #{id}")
SysRole selectById2(Long id);

3.1.2  使用resultMap方式

XML映射文件中的 resultMap 元素有一个对应的 Java 注解@Results ,使用这个注解来实现属性映射,新增一个 selectById2 方法,代码如下 。

从MyBatis 3.3.1版本开始,@Results注解增加了一个id属性,设置了id属性后,就可以通过id属性引用同一个@Results配置了。

    @Results(id = "roleResultMap", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "roleName", column = "role_name"),
@Result(property = "enabled", column = "enabled"),
@Result(property = "createBy", column = "create_by"),
@Result(property = "createTime", column = "create_time")
})
@Select("select id,role_name, enabled, create_by, create_time from sys_role where id = #{id}")
SysRole selectById2(Long id);

这里的@Result 注解对应着 XML 文件中的 <result> 元素,而参数中写上 id=true 时就对应 <id> 元素(声明为主键)。

如何引用这个@Results呢?新增一个 selectAll方法,代码如下。

    @ResultMap("roleResultMap")
@Select("select * from sys_role")
List<SysRole> selectAll();

注意: 使用@ResultMap注解引用即可,当配合着使用XML配置方式的时候,还可以是XML中 <resultMap>元素的id属性值。

在 RoleMapperTest 中写出以上示例方法的测试方法。selectById 方法的测试代码如下。

BaseMapperTest.java

/**
* 基础测试类
*/
public class BaseMapperTest {
private static SqlSessionFactory sqlSessionFactory; @BeforeClass
public static void init(){
try {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
reader.close();
} catch (IOException ignore) {
ignore.printStackTrace();
}
} public SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
} }

testSelectById、testSelectById2、testSelectAll

方法的测试代码如下:

public class RoleMapperTest extends BaseMapperTest {

    @Test
public void testSelectById(){
//获取 sqlSession
SqlSession sqlSession = getSqlSession();
try {
//获取 RoleMapper 接口
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
//调用 selectById 方法,查询 id = 1 的角色
SysRole role = roleMapper.selectById(1l);
//role 不为空
Assert.assertNotNull(role);
//roleName = 管理员
Assert.assertEquals("管理员", role.getRoleName());
} finally {
//不要忘记关闭 sqlSession
sqlSession.close();
}
} @Test
public void testSelectById2(){
//获取 sqlSession
SqlSession sqlSession = getSqlSession();
try {
//获取 RoleMapper 接口
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
//调用 selectById 方法,查询 id = 1 的角色
SysRole role = roleMapper.selectById2(1l);
//role 不为空
Assert.assertNotNull(role);
//roleName = 管理员
Assert.assertEquals("管理员", role.getRoleName());
} finally {
//不要忘记关闭 sqlSession
sqlSession.close();
}
} @Test
public void testSelectAll(){
SqlSession sqlSession = getSqlSession();
try {
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
//调用 selectAll 方法查询所有角色
List<SysRole> roleList = roleMapper.selectAll();
//结果不为空
Assert.assertNotNull(roleList);
//角色数量大于 0 个
Assert.assertTrue(roleList.size() > 0);
//验证下划线字段是否映射成功
Assert.assertNotNull(roleList.get(0).getRoleName());
} finally {
//不要忘记关闭 sqlSession
sqlSession.close();
}
} // @Test
// public void testSelectAllRoleAndPrivileges(){
// //获取 sqlSession
// SqlSession sqlSession = getSqlSession();
// try {
// //获取 RoleMapper 接口
// RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
// List<SysRole> roleList = roleMapper.selectAllRoleAndPrivileges();
// for(SysRole role: roleList){
// System.out.println("角色名:" + role.getRoleName());
// for(SysPrivilege privilege : role.getPrivilegeList()){
// System.out.println("权限名:" + privilege.getPrivilegeName());
// }
// }
// } finally {
// //不要忘记关闭 sqlSession
// sqlSession.close();
// }
// }
//
// @Test
// public void testSelectRoleByUserIdChoose(){
// //获取 sqlSession
// SqlSession sqlSession = getSqlSession();
// try {
// //获取 RoleMapper 接口
// RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
// //由于数据库数据 enable 都是 1,所以我们给其中一个角色的 enable 赋值为 0
// SysRole role = roleMapper.selectById(2L);
// role.setEnabled(Enabled.disabled);
// roleMapper.updateById(role);
// //获取用户 1 的角色
// List<SysRole> roleList = roleMapper.selectRoleByUserIdChoose(1L);
// for(SysRole r: roleList){
// System.out.println("角色名:" + r.getRoleName());
// if(r.getId().equals(1L)){
// //第一个角色存在权限信息
// Assert.assertNotNull(r.getPrivilegeList());
// } else if(r.getId().equals(2L)){
// //第二个角色的权限为 null
// Assert.assertNull(r.getPrivilegeList());
// continue;
// }
// for(SysPrivilege privilege : r.getPrivilegeList()){
// System.out.println("权限名:" + privilege.getPrivilegeName());
// }
// }
// } finally {
// sqlSession.rollback();
// //不要忘记关闭 sqlSession
// sqlSession.close();
// }
// }
//
// @Test
// public void testUpdateById(){
// //获取 sqlSession
// SqlSession sqlSession = getSqlSession();
// try {
// //获取 RoleMapper 接口
// RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
// //由于数据库数据 enable 都是 1,所以我们给其中一个角色的 enable 赋值为 0
// SysRole role = roleMapper.selectById(2L);
// Assert.assertEquals(Enabled.enabled, role.getEnabled());
// role.setEnabled(Enabled.disabled);
// roleMapper.updateById(role);
// } finally {
// sqlSession.rollback();
// //不要忘记关闭 sqlSession
// sqlSession.close();
// }
// }
//
// @Test
// public void testSelectAllByRowBounds(){
// SqlSession sqlSession = getSqlSession();
// try {
// RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
// //查询前两个,使用 RowBounds 类型不会查询总数
// RowBounds rowBounds = new RowBounds(0, 1);
// List<SysRole> list = roleMapper.selectAll(rowBounds);
// for(SysRole role : list){
// System.out.println("角色名:" + role.getRoleName());
// }
// //使用 PageRowBounds 会查询总数
// PageRowBounds pageRowBounds = new PageRowBounds(0, 1);
// list = roleMapper.selectAll(pageRowBounds);
// //获取总数
// System.out.println("查询总数:" + pageRowBounds.getTotal());
// for(SysRole role : list){
// System.out.println("角色名:" + role.getRoleName());
// }
// //再次查询
// pageRowBounds = new PageRowBounds(1, 1);
// list = roleMapper.selectAll(pageRowBounds);
// //获取总数
// System.out.println("查询总数:" + pageRowBounds.getTotal());
// for(SysRole role : list){
// System.out.println("角色名:" + role.getRoleName());
// }
// } finally {
// sqlSession.close();
// }
// } }

public class RoleMapperTest extends BaseMapperTest

3.2  @Insert注解

@Insert 注解本身是简单的,但如果需要返回主键的值,情况会变得稍微复杂一些。

3.2.1  不需要返回主键

这个方法和XML中的SQL完全一样,这里不做特别介绍,代码如下。

    @Insert({"insert into sys_role(id, role_name, enabled, create_by, create_time)",
"values(#{id}, #{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})"})
int insert(SysRole sysRole);

3.2.2  返回自增主键

新增 insert2方法,代码如下。

    @Insert({"insert into sys_role(role_name, enabled, create_by, create_time)",
"values(#{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})"})
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert2(SysRole sysRole);

和上面的insert方法相比,insert2方法中的SQL中少了 id一列,注解多了一个

@Options ,我们在这个注解中设置了 useGeneratedKeys 和 keyProperty 属性,用法和XML相同,

当需要配置多个列时,这个注解也提供了 keyColumn 属性,可以像XML中那样配置使用。

3.2.3  返回非自增主键

新增 insert3 方法,代码如下。

    @Insert({"insert into sys_role(role_name, enabled, create_by, create_time)",
"values(#{roleName}, #{enabled}, #{createBy}, #{createTime, jdbcType=TIMESTAMP})"})
@SelectKey(statement = "SELECT LAST_INSERT_ID()",
keyProperty = "id",
resultType = Long.class,
before = false)
int insert3(SysRole sysRole);

使用@SelectKey注解,以下代码是前面XML中配置的 selectKey

        <selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>

来对比一下,配置属性基本上都是相同的,其中 before 为 false 时功能等同于 order="AFTER",before 为 true 时功能等同于 order="BEFORE"。

3.3 @Update 和@Delete注解

@Update 注解和@Delete 注解的用法可以用以下示例来进行说明。

在 RoleMapper 中新增 updateById 和 deleteById 方法,代码如下:

    @Update({"update sys_role",
"set role_name = #{roleName},",
"enabled = #{enabled},",
"create_by = #{createBy},",
"create_time = #{createTime, jdbcType=TIMESTAMP}",
"where id = #{id}"
})
int updateById(SysRole sysRole); @Delete("delete from sys_role where id = #{id}")
int deleteById(Long id);

大家可以参考 UserMapperTest 中的例子自行写出相应的测试代码,此处不做详细说明。


3.4 Provider 注解

除了上面 4 种注解可以使用简单的 SQL 外,MyBatis 还提供了 4 种 Provider 注解,分别是 @SelectProvider 、 @InsertProvider 、 @UpdateProvider 和 @DeleteProvider 。它们同样可以实现查询、插入、更新、删除操作。

下面通过 @SelectProvider 用法来了解 Provider 注解方式的基本用法。

创建 PrivilegeMapper 接口,添加 selectById 方法,代码如下。

    @SelectProvider(type = PrivilegeProvider.class, method = "selectById")
SysPrivilege selectById(Long id);

cn.bjut.simple.provider.PrivilegeProvider

其中 PrivilegeProvider 类代码如下。

    public String selectById(final Long id){
return new SQL(){
{
SELECT("id, privilege_name, privilege_url");
FROM("sys_privilege");
WHERE("id = #{id}");
}
}.toString();
}

Provider 的注解中提供了两个必填属性 type 和 method。type 配置的是一个包含 method 属性指定方法的类,这个类必须有空的构造方法,这个方法的值就是要执行的 SQL 语句,并且 method 属性指定的方法的返回值必须是 String 类型。

还可以直接返回 SQL 字符串,代码如下。

    public String selectAll(){
return "select * from sys_privilege";
}

对于以上两种写法,大家可以根据自己的需求来选择其中的任意一种,SQL 较长或需要拼接时推荐使用 new SQL()的方式。以下是 selectById 方法的测试代码。

PrivilegeMapperTest

public class PrivilegeMapperTest extends BaseMapperTest {

    @Test
public void testSelectById(){
//获取 sqlSession
SqlSession sqlSession = getSqlSession();
try {
//获取 PrivilegeMapper 接口
PrivilegeMapper privilegeMapper = sqlSession.getMapper(PrivilegeMapper.class);
//调用 selectById 方法,查询 id = 1 的权限
SysPrivilege privilege = privilegeMapper.selectById(1l);
//privilege 不为空
Assert.assertNotNull(privilege);
//privilegeName = 管理员
Assert.assertEquals("用户管理", privilege.getPrivilegeName());
} finally {
//不要忘记关闭 sqlSession
sqlSession.close();
}
}

public void testSelectById()


3.5 多表关联查询的@One 和@Many注解

数据库使用:oracle

图形化界面:PL/SQL Developer

案例的介绍:一个销售旅游产品的网站。

查询的要求:根据订单的ID查询,订单的详细信息 ,这是一个多表关联查询。使用mybatis注解+接口实现SSM整合,把查询结果反馈到JSP。

Product

public class Product {

    private String id; // 主键
private String productNum; // 编号 唯一
private String productName; // 名称
private String cityName; // 出发城市
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
private Date departureTime; // 出发时间
private String departureTimeStr; //为了页面显示,数据库里没有的字段
private double productPrice; // 产品价格
private String productDesc; // 产品描述
private Integer productStatus; // 状态 0 关闭 1 开启
private String productStatusStr; //为了页面显示,数据库里没有的字段 //部分实体类的代码省略get/set方法
Orders

//订单表的实体类
public class Orders {
private String id;
private String orderNum;
private Date orderTime;
private String orderTimeStr;
private int orderStatus;
private int peopleCount;
private Product product;
private List<Traveller> travellers;
private Member member;
private Integer payType;
private String payTypeStr;
private String orderDesc;
private String orderStatusStr;
//============================================
//通过在SET方法的方法体里直接赋值字符串内容实现
public String getOrderStatusStr() {
//订单状态 (0未支付 1已支付)
if(orderStatus ==0){
orderStatusStr= "未支付";
}else if (orderStatus ==1){
orderStatusStr= "已支付";
}
return orderStatusStr;
}
//=============================================
//以下省略一些GET/SET方法



package cn.bjut.ssm.dao;

import cn.bjut.ssm.domain.Traveller;
import org.apache.ibatis.annotations.Select; import java.util.List; public interface ITravellerDao {
@Select("select * from traveller where id in ( select travellerId from order_traveller where orderId=#{ordersId})")
public List<Traveller> findByOrdersId(String ordersId) throws Exception; }

package cn.bjut.ssm.dao;

import cn.bjut.ssm.domain.Member;
import org.apache.ibatis.annotations.Select; public interface IMemberDao { //通过订单ID查询会员,目的是供订单查询的@Select下@Result注解引用
@Select("select * from MEMBER where id=#{memberId}")
Member findById(String memberId) throws Exception;
}
    //通过订单主键ID查询订单详情(多表关联查询)
@Select("select * from ORDERS where id = #{ordersId}" ) //oracle数据库TABLE名不区分大小写
@Results({ //为了网页显示的后缀Str类型的实体类属性不用对应出来
@Result(property ="id",column = "id",id = true ), //主键声明id = true
@Result(property ="orderNum",column = "orderMum"),
@Result(property ="orderTime",column = "orderTime"),
@Result(property ="orderStatus",column = "orderStatus"),
@Result(property ="peopleCount",column = "peopleCount"),
@Result(property ="payType",column = "payType"),
@Result(property ="orderDesc",column = "orderDesc"),
//多表关联查询,声明“引用实体类”的封装通过:javaType= Xxx实体类.class
@Result(property ="product",column = "productId",javaType = Product.class ,one =@One(select = "cn.bjut.ssm.dao.IProductDao.findById")),
@Result(property ="member",column = "memberId",javaType = Member.class ,one =@One(select = "cn.bjut.ssm.dao.IMemberDao.findById")),
//通过中间表查询多对多关系,返回一个其它实体类的List集合
@Result(property = "travellers",column ="id",javaType = java.util.List.class,many = @Many(select = "cn.bjut.ssm.dao.ITravellerDao.findByOrdersId"))
})
public Orders findById(String ordersId)throws Exception;
 

======================================================================================

end

MyBatis从入门到精通(第3章):MyBatis注解方式的基本使用的更多相关文章

  1. MyBatis从入门到精通(第5章):5.4 Example 介绍

    jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.Eclipse Version: 2019-12 M2 (4.14.0) MyBatis从入门到精通(第5章):MyBatis代码 ...

  2. MyBatis从入门到精通(第9章):Spring集成MyBatis(下)

    MyBatis从入门到精通(第9章):Spring集成MyBatis(下) springmvc执行流程原理 mybatis-spring  可以帮助我们将MyBatis代码无缝整合到Spring中.使 ...

  3. MyBatis从入门到精通(第9章):Spring集成MyBatis(中)

    MyBatis从入门到精通(第9章):Spring集成MyBatis(中) 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法.应该将应用自身的设计和具体 ...

  4. MyBatis从入门到精通(第9章):Spring集成MyBatis(上)

    MyBatis从入门到精通(第9章):Spring集成MyBatis(上) Spring是一个为了解决企业级Web应用开发过程中面临的复杂性,而被创建的一个非常流行的轻量级框架. mybatis-sp ...

  5. MyBatis从入门到精通(第5章):MyBatis代码生成器

    jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.Eclipse Version: 2019-12 M2 (4.14.0) MyBatis从入门到精通(第5章):MyBatis代码 ...

  6. MyBatis从入门到精通(第4章):MyBatis动态SQL【foreach、bind、OGNL用法】

    (第4章):MyBatis动态SQL[foreach.bind.OGNL用法] 4.4 foreach 用法 SQL 语句中有时会使用 IN 关键字,例如 id in (1,2,3).可以使用 ${i ...

  7. MyBatis从入门到精通:第一章实体类与Mapper.xml文件

    实体类: package tk.mybatis.simple.model; public class Country { public Long getId() { return id; } publ ...

  8. MyBatis从入门到精通:第一章配置MyBatis

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...

  9. MyBatis从入门到精通(第6章):MyBatis 高级查询->6.1.2高级结果映射之一对多映射

    jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.IntelliJ IDEA 2019.3.1 本章主要包含的内容为 MyBatis 的高级结果映射,主要处理数据库一对一.一对多的 ...

随机推荐

  1. day09-Python运维开发基础(函数收集参数、命名关键字参数与返回值、函数名的特殊使用及 全局/局部变量详解)

    1. 函数收集参数.命名关键字参数与返回值.函数名的特殊使用 # ### 默认形参 和 关键字实参 # 默认形参和 关键字实参 在写法上是一样 # 函数的定义处 """默 ...

  2. 学会使用Google hacking

    https://klionsec.github.io/2014/12/14/search-hacking/ 熟练利用Google hacking 来辅助我们快速渗透 http://www.sec-re ...

  3. MariaDB——相关概念与sql语句

    数据库变量   数据库的两个目录 数据存放目录:/var/lib/mysql/     配置文件目录:/etc/my.cnf.d/ 查看数据库的变量 show global variables lik ...

  4. 「LuoguP3979」遥远的国度

    传送门 Luogu 解题思路 带换根操作的树剖. 换根只会影响更新或查询子树信息的操作. 我们始终保持初始的根不变,然后只要分类讨论一下: 假设当前被查询的节点是 \(u\) 如果 \(u\) 就是根 ...

  5. Redis列表类型

    列表类型(list) 可以存储一个有序的字符串列表.常用的操作是向列表两端添加元素. 一个列表类型键最多能容纳2^32 -1个元素. 命令 向列表两端增加元素 LPUSH key value [val ...

  6. 图片转换到指定大小PDF

    1.首先转换为eps jpeg2ps compile to exec file ./jpeg2ps  -p a4  a.jpg -o x.eps2.从eps转换到pdf ps2pdf -dDownsa ...

  7. eot文件

    *.eot文件 是一种压缩字库,目的是解决在网页中嵌入特殊字体的难题2.在网页中嵌入的字体只能是 OpenType 类型,其他类型的字体只有转换成 OpenType 类型(eot格式)的字体才能在网页 ...

  8. SciPy 常量

    章节 SciPy 介绍 SciPy 安装 SciPy 基础功能 SciPy 特殊函数 SciPy k均值聚类 SciPy 常量 SciPy fftpack(傅里叶变换) SciPy 积分 SciPy ...

  9. 转《Python爬虫学习系列教程》学习笔记

    http://www.cnblogs.com/xin-xin/p/4297852.html

  10. MQTT 协议学习:002- 通信报文的构成

    背景 之前工作中参与有关协议调试的时候,发现对于协议帧的解析是比较重要的. 参考:<MQTT协议 -- 消息报文格式>.<基于STM32实现MQTT>.<MQTT协议从服 ...