1. 程式人生 > >mybatis基礎系列(二)——基礎語法、別名、輸入對映、輸出對映

mybatis基礎系列(二)——基礎語法、別名、輸入對映、輸出對映

增刪改查

<mapper>根節點及其子節點

mybatis框架需要讀取對映檔案建立會話工廠,對映檔案是以<mapper>作為根節點,在根節點中支援9個元素,分別為insert、update、delete、select(增刪改查);cache、cache-ref、resultMap、parameterMap、sql。如下圖:

image

名稱空間

<mapper>根節點有個屬性namespace,作用是對sql語句進行分類化管理。

<select>節點

佔位符#{}

一個<select>代表一條查詢語句,<select>常用屬性有id,parameterType,resultType。<select>節點的內容為sql語句,其語法與平常寫的sql語句相似,不同的地方是條件引數可以通過佔位符#{}替換。例如:

sql語法:SELECT * FROM t_emp WHERE empno=’7369’

mybitis中的語法:SELECT * FROM t_emp WHERE empno=#{empno}

id:標誌對映檔案中的sql,通常id也稱為statement的id。id的值就是xxxMapper.java中的方法名。

parameterType:執行sql語句中的輸入引數的型別。

resultType:指定sql輸出結果對映成java型別的物件。

#{}:表示一個佔位符

#{id}:其中id表示接收輸入的引數,引數名稱就是id。#{}中的引數可以是任意物件。

示例與執行問題

EmpMapper.xml

<mapper namespace="com.itpsc.mapper.EmpMapper" >

<select id="queryById" parameterType="int" resultType="com.itpsc.entity.Emp">
  SELECT * FROM t_emp WHERE empno=#{empno}
</select>

</mapper>

 

//EmpMapper.java
public interface EmpMapper extends BaseMapper<Emp> {
    Emp queryById(Integer empno);
}

//EmpService.java
public interface EmpService { Emp queryById(Integer empno); } //EmpServiceImpl.java @Service public class EmpServiceImpl extends ServiceImpl<EmpMapper,Emp> implements EmpService { public EmpServiceImpl() { super(); }; public EmpServiceImpl(EmpMapper mapper) { this.baseMapper = mapper; }; @Override public Emp queryById(Integer empno) { return this.baseMapper.queryById(empno); } } //EmpTests.java @RunWith(SpringRunner.class) @SpringBootTest public class EmpTests { @Resource private EmpService empService; @Test public void contextLoads() { } @Test public void testQueryById() { System.out.println(empService.queryById(7369)); } }

執行報錯:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.itpsc.mapper.EmpMapper.queryById

找不到xml,發現在idea編譯後的classes路徑下並沒有相應的XML檔案。

image

因為IDEA在編譯的時候忽略掉了XML檔案,一個解決方法是將所有的XML檔案移動到Resource資料夾下,這樣在編譯的時候就會將XML檔案一起。

另一個方法是配置Maven不過濾src/main/java目錄下的.properties檔案和.xml檔案。

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
    </resource>
</resources>

再次編譯就看到了classes目錄下有xml檔案了。

image

執行test後輸出:

Emp{empno=7369, ename='SMITH', job='CLERK', mgr=7902, hiredate=Wed Dec 17 00:00:00 CST 1980, sal=800.0, comm=null, deptno=20}

拼接${}

${}:用來拼接sql字串,將接收到的引數內容不加任何修飾拼接在sql語句中。

sql語法:SELECT * FROM t_emp WHERE ename LIKE 'SMITH';

mybitis中的語法: SELECT * FROM t_emp WHERE ename LIKE '${_parameter}'

<select id="queryLikeName" parameterType="String" resultType="com.itpsc.entity.Emp">
  SELECT * FROM t_emp WHERE ename LIKE '${_parameter}'
</select>

注意

如果傳入的引數型別為String型別,則引數名需統一修改為_parameter,不能將引數設為bean裡的名稱。

否則執行報錯為:There is no getter for property named 'preCode' in 'class java.lang.String

 

<insert>節點

一個<insert>代表一條insert語句,和<select>節點一樣,<insert>其語法與平常寫的sql語句相似,不同的地方是條件引數可以通過佔位符#{}替換。

sql語法:insert into t_emp

(empno,ename,job,mgr,hiredate,sal,comm,deptno) values(7100,’itpsc’,’developer’,7902,’1980-12-10’,1000.00,200.00,20)

mybitis中的語法:

  <insert id="add" parameterType="com.itpsc.entity.Emp">

    insert into t_emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)

    values(#{empno},#{ename},#{job},#{mgr},#{hiredate,jdbcType=DATE},#{sal},#{comm},#{deptno})

  </insert>

注意日期型別

mybatis日期型別的欄位,要加jdbcType=DATE。否則會報錯:There is no getter for property named 'hirdate' in 'class com.itpsc.entity.Emp'。

Mybatis中javaType和jdbcType對應關係

JDBC Type           Java Type  
CHAR                String  
VARCHAR             String  
LONGVARCHAR         String  
NUMERIC             java.math.BigDecimal  
DECIMAL             java.math.BigDecimal  
BIT             boolean  
BOOLEAN             boolean  
TINYINT             byte  
SMALLINT            short  
INTEGER             int  
BIGINT              long  
REAL                float  
FLOAT               double  
DOUBLE              double  
BINARY              byte[]  
VARBINARY           byte[]  
LONGVARBINARY               byte[]  
DATE                java.sql.Date  
TIME                java.sql.Time  
TIMESTAMP           java.sql.Timestamp  
CLOB                Clob  
BLOB                Blob  
ARRAY               Array  
DISTINCT            mapping of underlying type  
STRUCT              Struct  
REF                         Ref  
DATALINK            java.net.URL

insert與非自增主鍵返回

有時候新增記錄之後,需要這條新增記錄的主鍵,以便業務使用,但是新增之後再將其查詢出來明顯不合理,效率也變低了。mybatis可以將insert的記錄的主鍵返回。使用mysql的uuid()函式生成主鍵,需要修改表中id欄位型別為string,長度設定成35位。

mybatis的<selectKey>可以幫我們實現。Insert之前先通過uuid()查詢到主鍵,將主鍵輸入到sql語句中。

UserMapper.xml

<mapper namespace="com.itpsc.mapper.UserMapper" >
    <insert id="adduser" parameterType="com.itpsc.entity.User">
        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
            SELECT uuid()
        </selectKey>
        insert into t_user(id,name,password,phone) values(#{id},#{name},#{password},#{phone})
    </insert>
</mapper>
測試
@Test
public void testAddUser() {
    User user = new User();
    //user.setId(1L);
    user.setName("uuid name1");
    user.setPassword("98764");
    user.setPhone("13877711111");
    System.out.println(userService.adduser(user));
    System.out.println(user.getId());
}

輸出:

1
9a64919e-b02f-11e8-8b1f-f48e38ec6bad

insert與自增主鍵返回

再將user表中的id欄位修改為Bigint型別,並設為自增。User.java中id修改為Long型別。通過mysql函式LAST_INSERT_ID()獲取到剛插入記錄的自增主鍵,是insert之後呼叫此函式。

<insert id="getIdAfterAdduser" parameterType="com.itpsc.entity.User">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into t_user(name,password,phone) values(#{name},#{password},#{phone})
</insert>
@Test
public void getIdAfterAdduser() {
    User user = new User();
    user.setName("auto add id");
    user.setPassword("98764");
    user.setPhone("13877711111");
    System.out.println(userService.getIdAfterAdduser(user));
    System.out.println(user.getId());
}

執行輸出:
1
3

<delete>節點

sql語法:delete FROM t_user WHERE id=3

mybitis中的語法:delete FROM t_user WHERE id=#{id}

<delete id="delete" parameterType="java.lang.Long">
        DELETE from t_user WHERE id=#{id}
</delete>

 

<update>節點

<update id="updateById" parameterType="com.itpsc.entity.User">
        UPDATE t_user set NAME=#{name},password=#{password},
phone=#{phone} WHERE id=#{id}
</update>

把整個java物件傳入,要更新哪些欄位sql語句決定。

 

佔位符與拼接符小結

#{}表示一個佔位符號,#{}接收輸入引數,型別可以是簡單型別也可以是複雜的資料型別。#{}接收物件值,通過OGNL語法(user.username)讀取物件中的屬性值。

${}表示一個拼接符號,會引用sql注入,不建議使用${}。${}接收輸入引數,型別可以是簡單型別也可以是複雜資料型別。${}接收物件值,通過OGNL語法(user.username)讀取物件中的屬性值。

出入參定義別名

批量定義別名

在mapper.xml中,定義很多的statement,statement需要parameterType指定輸入引數的型別、需要resultType指定輸出結果的對映型別。

如果在指定型別時輸入型別全路徑,不方便進行開發,可以針對parameterType或resultType指定的型別定義一些別名,在mapper.xml中通過別名定義,方便開發。

在springboot 的yml配置檔案中通過type-aliases-package定義別名,在對parameterType或resultType指定的型別中就可以省略包名。

mybatis-plus:
   mapper-locations: "classpath:com/itpsc/mapper/**/*.xml"
   type-aliases-package: "com.itpsc.entity"
   global-config:
      db-column-underline: true

<insert id="adduser" parameterType="User">
    insert into t_user(name,password,phone) values(#{name},#{password},#{phone})
</insert>

輸入對映

parameterType

 

前面我們學習的輸入都是簡單物件,如果輸入引數的型別是複雜物件(包裝物件),該怎麼寫呢。

<select id="queryList" 
parameterType="com.itpsc.request.EmpRequest" resultType="com.itpsc.vo.EmpVo">
    SELECT * FROM t_emp WHERE deptno=#{emp.deptno} and job=#{emp.job}
  </select>
public interface EmpMapper extends BaseMapper<Emp> {
//...
    EmpVo queryList(EmpRequest request);
}

public class EmpRequest{
    private Emp emp;
    //其它條件
    public Emp getEmp() {
        return emp;
    }
    public void setEmp(Emp emp) {
        this.emp = emp;
    }
}


public class EmpVo extends Emp{
//...
}

使用parameterType進行輸入對映,型別是包裝物件,但是佔位符裡用的是被包裝物件的屬性。通過#{emp.deptno}取出被包裝物件的deptno屬性。

 

輸出對映

resultType

執行測試方法報錯:

org.mybatis.spring.MyBatisSystemException:
 nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

返回資料型別由xxxMapper.java介面中宣告的方法的返回型別和xxxMapper.xml檔案共同決定。

如果mapper方法返回單個物件(非集合物件),代理物件內部通過selectOne查詢資料庫。

如果mapper方法返回集合物件,代理物件內部通過selectList查詢資料庫。不論是返回單一物件還是物件列表,xxxMapper.xml中的配置都是一樣的,都是resultMap=”***Map”或resultType=“* .* .*”型別。

例如:

返回單個物件resultType的值為"com.itpsc.entity.Emp"

返回多個物件resultType的值也是"com.itpsc.entity.Emp"

所以將mapper方法的返回型別宣告為List<Emp>即可。

使用resultType進行輸出對映,只有查詢出來的列名和物件中的屬性名一致,該列才可以對映成功。如果查詢出來的列名和物件中的屬性名全部不一致,沒有建立物件。只要查詢出來的列名和物件中的屬性有一個一致,就會建立物件。

如果我們把上面例子中的EmpVo 不繼承Emp,查詢出來就不建立物件。如下圖

image

image

 

resultMap

如果查詢出來的列名和物件的屬性名不一致,通過定義一個resultMap對列名和物件屬性名之間作一個對映關係。

1、定義resultMap

2、使用resultMap作為statement的輸出對映型別

 

<resultMap id="userMap" type="com.itpsc.entity.Emp" >
    <result column="_empno" property="empnum" jdbcType="INTEGER" />
    <result column="_ename" property="ename" jdbcType="VARCHAR" />
    <result column="_job" property="job" jdbcType="VARCHAR" />
    <result column="_mgr" property="mgr" jdbcType="INTEGER" />
    <result column="_hiredate" property="hiredate" jdbcType="DATE" />
    <result column="_sal" property="sal" jdbcType="REAL" />
    <result column="_comm" property="comm" jdbcType="REAL" />
    <result column="_deptno" property="deptno" jdbcType="INTEGER" />
  </resultMap>

  <select id="queryById" parameterType="int"resultMap ="userMap">
    SELECT empno _empno,ename _ename,job _job,mgr _mgr,hiredate _hiredate,sal _sal,comm _comm,deptno _deptno FROM t_emp WHERE empno=#{empno}
  </select>

本篇到此結束,下篇預告mybatis基礎系列(三)——動態sql。