MyBatis 注解方式就是將 SQL 語句直接寫在DAO層的接口上。
在黑馬2018年錄制的雙元視頻課:\08 SSM整合案例【企業權限管理系統】\07.訂單操作 有使用mybatis注解進行多表關聯查詢的案例,在下文會有使用注解的補充說明。
這種方式的優點是 , 對於需求比較簡單的系統,效率較高。缺點是 ,當 SQL 有變化時都需要重新編譯代碼, 一般情況下不建議使用MyBatis的注解方式 。
因此, (原書)本章不會進行深入講解。 在 MyBatis 注解 SQL 中,最基本的就是@Select 、@Insert 、@Update 和@Delete 四種 。 下面以 RoleMapper 為例,對這幾個注解的用法進行講解 。
-- ---------------------------- -- Table structure for sys_role -- ---------------------------- DROP TABLE IF EXISTS `sys_role`; CREATE TABLE `sys_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_name` varchar(50) DEFAULT NULL COMMENT '角色名', `enabled` int(11) DEFAULT NULL COMMENT '有效標志', `create_by` bigint(20) DEFAULT NULL COMMENT '創建人', `create_time` datetime DEFAULT NULL COMMENT '創建時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='角色表'; -- ---------------------------- -- Records of sys_role -- ---------------------------- INSERT INTO `sys_role` VALUES ('1', '管理員', '1', '1', '2016-04-01 17:02:14'); INSERT INTO `sys_role` VALUES ('2', '普通用戶', '1', '1', '2016-04-01 17:02:34');pom.xml
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build>
3.1 @Select 注解
在 cn.bjut.example.mapper.RoleMapper 接口中添加如下注解方法 。
package cn.bjut.example.mapper;
import cn.bjut.example.model.SysRole;
import org.apache.ibatis.annotations.Select;
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 配置
- 以及使用 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 方法,代碼如下 。
@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);
從MyBatis 3.3.1版本開始,@Results注解增加了一個id屬性,設置了id屬性后,就可以通過id屬性引用同一個@Results配置了,
示例代碼如上:
如何引用這個@Results呢?新增一個 selectAll方法,代碼如下。
@ResultMap("roleResultMap")
@Select("select * from sys_role")
List<SysRole> selectAll();
注意: 使用@ResultMap注解引用即可,當配合着使用XML配置方式的時候,還可以是XML中 <resultMap>元素的id屬性值。
在RoleMapperTest 中寫出以上示例方法的測試方法。
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();
}
}
}
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>
3.3 @Update 和@Delete注解
@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);
3.4 多表關聯查詢的@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

