1. 程式人生 > >MyBatis-06 MyBatis XML方式之多個介面引數用法

MyBatis-06 MyBatis XML方式之多個介面引數用法

概述

我們前面幾篇文章,所列舉的介面方法中只有一個引數,引數的型別可以分為兩種

1. 基本型別

2. JavaBean

當引數是一個基本型別的時候,它在XML檔案中對應的SQL語句只會使用一個引數,比如delete方法

當引數型別是一個JavaBean型別的時候,它在XML檔案中對應的SQL語句中會有多個引數,比如 insert 、 update

在實際應用中使用多個引數的情況再常見不過來。 前面的博文,我們有個方法是將多個引數合併到一個JavaBean中,並使用這個Bean作為介面方法的引數。 這種方法雖然方便,單並不適合全部的情況,因為不可能只為了兩三個引數去建立新的JavaBean。

因此對於引數比較少的情況,有兩種方式可以採用

  • 使用Map型別作為引數
  • 使用@Param註解

使用Map型別作為引數需要手工建立MAP並且對引數進行賦值,並不簡潔。這裡推薦使用@Param註解的方式

模擬場景:根據使用者ID和角色的enable狀態來查詢使用者的所有角色

工程結構

這裡寫圖片描述

多個基本型別引數的場景

我們先來看下如果在介面中使用多個引數但不是用Param註解的情況

1.UserRoleMapper介面增加介面方法

package com.artisan.mybatis.xml.mapper;

import java.util.List;

import
com.artisan.mybatis.xml.domain.SysRole; public interface UserRoleMapper { /** * * * @Title: selectSysRolesByUserIdAndRoleEnable * * @Description: 根據使用者ID和角色的Enable屬性查詢角色 * * @param userId * @param enable * @return * * @return: List<SysRole> */
List<SysRole> selectSysRolesByUserIdAndRoleEnable(Long userId, Integer enable); }

2.UserRoleMapper.xml配置查詢語句

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
                    "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<!-- 當Mapper介面和XML檔案關聯的時候, namespace的值就需要配置成介面的全限定名稱 -->                    
<mapper namespace="com.artisan.mybatis.xml.mapper.UserRoleMapper">

    <select 
        id="selectSysRolesByUserIdAndRoleEnable" 
        resultType="com.artisan.mybatis.xml.domain.SysRole">
        SELECT
            c.id,
            c.role_name roleName,
            c.enabled,
            c.create_by createBy,
            c.create_time createTime
        FROM
            sys_user a
        INNER JOIN sys_user_role b ON a.id = b.user_id
        INNER JOIN sys_role c ON b.role_id = c.id
        WHERE
            a.id = #{userId}
        AND c.enabled = #{enabled}
    </select>

</mapper>                   

3.單元測試

package com.artisan.mybatis.xml.mapper;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Test;

import com.artisan.mybatis.xml.domain.SysRole;

/**
 * 
 * 
 * @ClassName: UserRoleMapperTest
 * 
 * @Description: UserRoleMapperTest測試類
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年4月16日 下午3:34:45
 */
public class UserRoleMapperTest extends BaseMapperTest {

    private static final Logger logger = Logger.getLogger(UserRoleMapperTest.class);
    private SqlSession sqlSession;
    @Test
    public void selectSysRolesByUserIdAndRoleEnableTest(){

        logger.info("selectSysRolesByUserIdAndRoleEnableTest");

        try {
            // 獲取SqlSession
            sqlSession = getSqlSession();
            // 獲取UserRoleMapper
            UserRoleMapper userRoleMapper = sqlSession.getMapper(UserRoleMapper.class);

            // 通過介面呼叫方法
            List<SysRole> roleList = userRoleMapper.selectSysRolesByUserIdAndRoleEnable((long) 1, 1);

            // 期望roleList不為空
            Assert.assertNotNull(roleList);
            // 期望roleList> 0
            Assert.assertTrue(roleList.size() > 0);

            for (SysRole sysRole : roleList) {
                logger.info(sysRole);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            sqlSession.close();
            logger.info("sqlSession close successfully ");
        }
    }

}

選中方法,執行單元測試

會得到如下異常

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2]
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:122)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:122)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:64)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
    at com.sun.proxy.$Proxy6.selectSysRolesByUserIdAndRoleEnable(Unknown Source)
    at com.artisan.mybatis.xml.mapper.UserRoleMapperTest.selectSysRolesByUserIdAndRoleEnableTest(UserRoleMapperTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2]
    at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:168)
    at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)
    at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
    at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:212)
    at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:139)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:120)

重點資訊

### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userId' not found. Available parameters are [1, 0, param1, param2]

這個錯誤標識,xml可用的引數只有 0、1、param1、param2 , 沒有userId.

1, 0, param1, param2 都是Mybatis根據引數位置自定義的名字,如果將xml中的#{userId} 改為#{0}或者#{param1} ,將#{enbale}改為#{1}或者#{param2} ,這個方法是可以被正常呼叫的 。 這裡只是為了演示,實際上並不建議這麼做。

這裡寫圖片描述

這裡寫圖片描述

修復

現在在介面方法的引數前新增@Param註解

package com.artisan.mybatis.xml.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.artisan.mybatis.xml.domain.SysRole;

public interface UserRoleMapper {

    /**
     * 
     * 
     * @Title: selectSysRolesByUserIdAndRoleEnable
     * 
     * @Description: 根據使用者ID和角色的Enable屬性查詢角色
     * 
     * @param userId
     * @param enable
     * @return
     * 
     * @return: List<SysRole>
     */
    List<SysRole> selectSysRolesByUserIdAndRoleEnable(@Param("userId") Long userId, @Param("enable") Integer enable);


}

重新執行單元測試

2018-04-16 19:43:22,404  INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully
2018-04-16 19:43:22,408  INFO [main] (BaseMapperTest.java:29) - reader close successfully
2018-04-16 19:43:22,412  INFO [main] (UserRoleMapperTest.java:31) - selectSysRolesByUserIdAndRoleEnableTest
2018-04-16 19:43:22,910 DEBUG [main] (BaseJdbcLogger.java:142) - ==>  Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? AND c.enabled = ? 
2018-04-16 19:43:22,988 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long), 1(Integer)
2018-04-16 19:43:23,025 TRACE [main] (BaseJdbcLogger.java:148) - <==    Columns: id, roleName, enabled, createBy, createTime
2018-04-16 19:43:23,026 TRACE [main] (BaseJdbcLogger.java:148) - <==        Row: 1, 管理員, 1, 1, 2018-04-13 21:12:46.0
2018-04-16 19:43:23,031 TRACE [main] (BaseJdbcLogger.java:148) - <==        Row: 2, 普通使用者, 1, 1, 2018-04-13 21:12:46.0
2018-04-16 19:43:23,032 DEBUG [main] (BaseJdbcLogger.java:142) - <==      Total: 2
2018-04-16 19:43:23,033  INFO [main] (UserRoleMapperTest.java:48) - SysRole [id=1, roleName=管理員, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]
2018-04-16 19:43:23,037  INFO [main] (UserRoleMapperTest.java:48) - SysRole [id=2, roleName=普通使用者, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]
2018-04-16 19:43:23,039  INFO [main] (UserRoleMapperTest.java:54) - sqlSession close successfully 

測試通過了,這時的XML檔案中對應的SQL的可用引數變成了【userId,enable,param1,param2】 ,如果#{userId} 改為#{param1} ,將#{enbale}改為#{param2}也是可以通過的。

給引數配置@Param註解後,mybatis就會自動將引數封裝成Map型別,@Param註解值會作為Map的key 因此在SQL部分就會通過配置的註解值來使用引數。

多個引數為JavaBean型別場景

1. UserRoleMapper介面增加介面方法

// 傳遞多個JavaBean的場景
    List<SysRole> selectSysRolesByUserIdAndRoleEnable2(@Param("sysUser") SysUser sysUser, @Param("sysRole") SysRole sysRole);

這個時候,在XML中就不能直接使用#{userId}和#{enabled}了,而是要通過點取值方式使用 #{sysUser.id} 和 #{sysRole.enabled} 從兩個JavaBean中取出指定屬性的值。

2.UserRoleMapper.xml配置SQL

<!-- 傳遞多個Bean的場景 -->    
    <select 
        id="selectSysRolesByUserIdAndRoleEnable2" 
        resultType="com.artisan.mybatis.xml.domain.SysRole">
        SELECT
            c.id,
            c.role_name roleName,
            c.enabled,
            c.create_by createBy,
            c.create_time createTime
        FROM
            sys_user a
        INNER JOIN sys_user_role b ON a.id = b.user_id
        INNER JOIN sys_role c ON b.role_id = c.id
        WHERE
            a.id = #{sysUser.id}
        AND c.enabled = #{sysRole.enabled}
    </select>

3.單元測試

@Test
    public void selectSysRolesByUserIdAndRoleEnable2Test() {

        logger.info("selectSysRolesByUserIdAndRoleEnable2Test");

        try {
            // 獲取SqlSession
            sqlSession = getSqlSession();
            // 獲取UserRoleMapper
            UserRoleMapper userRoleMapper = sqlSession.getMapper(UserRoleMapper.class);

            // 構造SysUser
            SysUser sysUser = new SysUser();
            sysUser.setId((long) 1);
            // 構造SysRole
            SysRole sysRole = new SysRole();
            sysRole.setEnabled(1);

            // 通過介面呼叫方法
            List<SysRole> roleList = userRoleMapper.selectSysRolesByUserIdAndRoleEnable2(sysUser, sysRole);

            // 期望roleList不為空
            Assert.assertNotNull(roleList);
            // 期望roleList> 0
            Assert.assertTrue(roleList.size() > 0);

            for (SysRole sysRole2 : roleList) {
                logger.info(sysRole2);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            sqlSession.close();
            logger.info("sqlSession close successfully ");
        }
    }

日誌

2018-04-16 20:13:06,731  INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully
2018-04-16 20:13:06,734  INFO [main] (BaseMapperTest.java:29) - reader close successfully
2018-04-16 20:13:06,738  INFO [main] (UserRoleMapperTest.java:62) - selectSysRolesByUserIdAndRoleEnable2Test
2018-04-16 20:13:07,361 DEBUG [main] (BaseJdbcLogger.java:142) - ==>  Preparing: SELECT c.id, c.role_name roleName, c.enabled, c.create_by createBy, c.create_time createTime FROM sys_user a INNER JOIN sys_user_role b ON a.id = b.user_id INNER JOIN sys_role c ON b.role_id = c.id WHERE a.id = ? AND c.enabled = ? 
2018-04-16 20:13:07,449 DEBUG [main] (BaseJdbcLogger.java:142) - ==> Parameters: 1(Long), 1(Integer)
2018-04-16 20:13:07,483 TRACE [main] (BaseJdbcLogger.java:148) - <==    Columns: id, roleName, enabled, createBy, createTime
2018-04-16 20:13:07,484 TRACE [main] (BaseJdbcLogger.java:148) - <==        Row: 1, 管理員, 1, 1, 2018-04-13 21:12:46.0
2018-04-16 20:13:07,487 TRACE [main] (BaseJdbcLogger.java:148) - <==        Row: 2, 普通使用者, 1, 1, 2018-04-13 21:12:46.0
2018-04-16 20:13:07,488 DEBUG [main] (BaseJdbcLogger.java:142) - <==      Total: 2
2018-04-16 20:13:07,489  INFO [main] (UserRoleMapperTest.java:86) - SysRole [id=1, roleName=管理員, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]
2018-04-16 20:13:07,494  INFO [main] (UserRoleMapperTest.java:86) - SysRole [id=2, roleName=普通使用者, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]
2018-04-16 20:13:07,495  INFO [main] (UserRoleMapperTest.java:92) - sqlSession close successfully 

小結

除了上述常用的型別,介面的引數還可以是集合或者陣列,後續介紹。