增刪改查mapper根節點及其子節點mybatis框架需要讀取映射文件創建會話工廠,映射文件是以作為根節點,在根節點中支持9個元素,分別為insert、update、delete、select(增刪改查);cache、cache-ref、resultMap、parameterMap、sql。如下圖:... ...
增刪改查
mapper根節點及其子節點
mybatis框架需要讀取映射文件創建會話工廠,映射文件是以<mapper>作為根節點,在根節點中支持9個元素,分別為insert、update、delete、select(增刪改查);cache、cache-ref、resultMap、parameterMap、sql。如下圖:
命名空間
<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文件。
因為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文件了。
運行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,查詢出來就不創建對象。如下圖
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。