• MyBatis从入门到精通(第3章):MyBatis注解方式的基本使用


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

    在黑马录制的2018年双元视频课:8 SSM整合案例【企业权限管理系统】7.订单操作  有使用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

    部分内容来自于学习编程期间收集于网络的免费分享资源和工作后购买的付费内容。
  • 相关阅读:
    Tomcat中的类是怎么被一步步加载的?
    Tomcat中的类是怎么被一步步加载的?
    Tomcat中的类是怎么被一步步加载的?
    redis- info调优入门-《每日五分钟搞定大数据》
    redis- info调优入门-《每日五分钟搞定大数据》
    redis- info调优入门-《每日五分钟搞定大数据》
    Lambda表达式详解
    2012年末工作中遇到的问题总结及感悟
    数据库表扩展字段设计思路
    数据库表扩展字段设计思路
  • 原文地址:https://www.cnblogs.com/MarlonKang/p/11491217.html
Copyright © 2020-2023  润新知