MyBatis从入门到精通(第3章):MyBatis注解方式的基本使用
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 语句使用别名来实现,上面的例子中已经使用过 。 除此之外还有另外两种方式分别是:
- 使用 mapUnderscoreToCamelCase 配置方式自动映射。(springboot使用通用Mapper启动器,就是默认true)
- 以及使用 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注解方式的基本使用的更多相关文章
- 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代码 ...
- MyBatis从入门到精通(第9章):Spring集成MyBatis(下)
MyBatis从入门到精通(第9章):Spring集成MyBatis(下) springmvc执行流程原理 mybatis-spring 可以帮助我们将MyBatis代码无缝整合到Spring中.使 ...
- MyBatis从入门到精通(第9章):Spring集成MyBatis(中)
MyBatis从入门到精通(第9章):Spring集成MyBatis(中) 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法.应该将应用自身的设计和具体 ...
- MyBatis从入门到精通(第9章):Spring集成MyBatis(上)
MyBatis从入门到精通(第9章):Spring集成MyBatis(上) Spring是一个为了解决企业级Web应用开发过程中面临的复杂性,而被创建的一个非常流行的轻量级框架. mybatis-sp ...
- MyBatis从入门到精通(第5章):MyBatis代码生成器
jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.Eclipse Version: 2019-12 M2 (4.14.0) MyBatis从入门到精通(第5章):MyBatis代码 ...
- MyBatis从入门到精通(第4章):MyBatis动态SQL【foreach、bind、OGNL用法】
(第4章):MyBatis动态SQL[foreach.bind.OGNL用法] 4.4 foreach 用法 SQL 语句中有时会使用 IN 关键字,例如 id in (1,2,3).可以使用 ${i ...
- MyBatis从入门到精通:第一章实体类与Mapper.xml文件
实体类: package tk.mybatis.simple.model; public class Country { public Long getId() { return id; } publ ...
- MyBatis从入门到精通:第一章配置MyBatis
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...
- MyBatis从入门到精通(第6章):MyBatis 高级查询->6.1.2高级结果映射之一对多映射
jdk1.8.MyBatis3.4.6.MySQL数据库5.6.45.IntelliJ IDEA 2019.3.1 本章主要包含的内容为 MyBatis 的高级结果映射,主要处理数据库一对一.一对多的 ...
随机推荐
- JuJu团队1月10号工作汇报
JuJu团队1月10号工作汇报 JuJu Scrum 团队成员 今日工作 剩余任务 困难 飞飞 fix出现的bug -- 无 婷婷 完善main.jl 训练流程 -- 无 恩升 绘图 -- 无 金 ...
- 新闻网大数据实时分析可视化系统项目——15、基于IDEA环境下的Spark2.X程序开发
1.Windows开发环境配置与安装 下载IDEA并安装,可以百度一下免费文档. 2.IDEA Maven工程创建与配置 1)配置maven 2)新建Project项目 3)选择maven骨架 4)创 ...
- 二、Navicat、IDEA、nopad、eclipse、excle工具使用、问题、快捷键
1.Navicat工具: 目的:本地数据库与远程数据库之间数据导入导出 步骤1:文件--新建oracle链接/mysql的连接 步骤2:工具-选项:将本地oracle的bin\oci.dll 的路径复 ...
- 解题报告:SP1043 GSS1
题目链接:SP1043 GSS1 - Can you answer these queries I 对,\(GSS\)毒瘤数据结构题,就是我在这篇文章中提到的紫题. 相对其他\(GSS\)题简单些,但 ...
- 输入、输出(iostream)
在一个程序当中输入和输出都扮演着重要的角色,所以掌握基本输入输出是入门一门语言所必不可少的.本文主要简单叙述java的输入和输出. package ios; import java.util.Scan ...
- 开通博客第一天 写一个hello world
申请的博客第一天便被批准了,有了一个和大家交流学习的园地.在今后的日子里期待一起进步.
- L/SQL Developer 和 instantclient客户端安装配置
PL/SQL Developer 和 instantclient客户端安装配置(图文) 一: PL/SQL Developer 安装 下载安装文件安装,我这里的版本号是PLSQL7.1.4.1391, ...
- Python 动态从文件中导入类或函数的方法
假设模块文件名是data_used_to_test.py,放在tests文件夹下 文件夹结构如下: project |-tests |-data_used_to_test.py 文件内包含一个test ...
- Jquery实现功能---购物车
//需求,勾选选项时,总价格要跟着变,点击添加数量,总价格也要跟着变,全部要动态变化 //代码如下 <!DOCTYPE html> <html> <head> &l ...
- greenplum 存储过程 索引信息
涉及的索引表 参考:http://blog.nbhao.org/1539.html pg_index pg_indexes pg_stat_all_indexes # 记录当前数据库中所有的索引的使用 ...