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 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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM