Mybatis框架,MyBatis(3):SQL映射

 2023-10-15 阅读 33 评论 0

摘要:前面學習了config.xml,下面就要進入MyBatis的核心SQL映射了,第一篇文章的時候,student.xml里面是這么寫的: 1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-

前面學習了config.xml,下面就要進入MyBatis的核心SQL映射了,第一篇文章的時候,student.xml里面是這么寫的:

1
2
3
4
5
6
7
8
9
10
11
<?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 namespace="com.xrq.StudentMapper">
????<select id="selectStudentById" parameterType="int" resultType="Student">
????????<![CDATA[
????????????select * from student where studentId = #{id}
????????]]>
????</select>
</mapper>

基于這個xml,進行擴展和學習。

Mybatis框架。為什么要使用<![CDATA[ ... ]]>?

上面的配置文件中,大家一定注意到了一個細節,就是SQL語句用<![CDATA[ ... ]]>這對標簽包含起來了,那么為什么要這么做呢?不妨把上面內容稍微修改一下:

1
2
3
4
5
6
7
8
9
<?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 namespace="com.xrq.StudentMapper">
????<select id="selectStudentById" parameterType="int" resultType="Student">
????????select * from student where studentId = #{id} or studentAge < 10 or studentAge > 20;
????</select>
</mapper>

當然這句SQL語句沒有任何含義,只是瞎寫的演示用而已,運行一下看一下結果:

1
2
3
4
5
6
Exception in thread "main" java.lang.ExceptionInInitializerError
????at com.xrq.test.MyBatisTest.main(MyBatisTest.java:9)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in student.xml
...

MyBatis。后面的異常信息就不列了。按理說很正常的一句SQL語句,怎么會報錯呢?仔細想來,錯誤的根本原因就是student.xml本身是一個xml文件,它并不是專門為MyBatis服務的,它首先具有xml文件的語法。因此,”< 10 or studentAge >”這段,會先被解析為xml的標簽,xml哪有這種形式的標簽的?所以當然報錯了。

所以,使用<![CDATA[ ... ]]>,它可以保證如論如何<![CDATA[ ... ]]>里面的內容都會被解析成SQL語句。因此,建議每一條SQL語句都使用<![CDATA[ ... ]]>包含起來,這也是一種規避錯誤的做法。

select

SQL映射中有幾個頂級元素,其中最常見的四個就是insert、delete、update、select,分別對應于增、刪、改、查,下面先對于select元素進行學習。

1、多條件查詢查一個結果

前面的select語句只有一個條件,下面看一下多條件查詢如何做,首先是student.xml:

1
2
3
4
5
<select id="selectStudentByIdAndName" parameterType="Student" resultType="Student">
????<![CDATA[
????????select * from student where studentId = #{studentId} and studentName = #{studentName};
????]]>
</select>

注意這里的parameter只能是一個實體類,然后參數要和實體類里面定義的一樣,比如studentId、studentName,MyBatis將會自動查找這些屬性,然后將它們的值傳遞到預處理語句的參數中去。

還有一個很重要的地方是,使用參數的時候使用了”#”,另外還有一個符號”$”也可以引用參數,使用”#”最重要的作用就是防止SQL注入

接著看一下Java代碼的寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Student selectStudentByIdAndName(int studentId, String studentName)
{
????SqlSession ss = ssf.openSession();
????Student student = null;
????try
????{
????????student = ss.selectOne("com.xrq.StudentMapper.selectStudentByIdAndName",
????????????????new Student(studentId, studentName, 0, null));
????}
????finally
????{
????????ss.close();
????}
????return student;
}

這里selectOne方法的第二個參數傳入一個具體的Student進去就可以了,運行就不演示了,結果沒有問題。

2、查詢多個結果

上面的演示查詢的是一個結果,對于select來說,重要的當然是查詢多個結果,查詢多個結果有相應的寫法,看一下:

1
2
3
4
5
6
<select id="selectAll" parameterType="int" resultType="Student" flushCache="false" useCache="true"
????timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
????<![CDATA[
????????select * from student where studentId > #{id};
????]]>
</select>

這里稍微玩了一些花樣,select里面多放了一些屬性,設置了每條語句的作用細節,分別解釋下這些屬性的作用:

  • id—-不說了,用來和namespace唯一確定一條引用的SQL語句
  • parameterType—-參數類型,如果SQL語句中的動態參數只有一個,這個屬性可有可無
  • resultType—-結果類型,注意如果返回結果是集合,應該是集合所包含的類型,而不是集合本身
  • flushCache—-將其設置為true,無論語句什么時候被調用,都會導致緩存被清空,默認值為false
  • useCache—-將其設置為true,將會導致本條語句的結果被緩存,默認值為true
  • timeout—-這個設置驅動程序等待數據庫返回請求結果,并拋出異常事件的最大等待值,默認這個參數是不設置的(即由驅動自行處理)
  • fetchSize—-這是設置驅動程序每次批量返回結果的行數,默認不設置(即由驅動自行處理)
  • statementType—-STATEMENT、PREPARED或CALLABLE的一種,這會讓MyBatis選擇使用Statement、PreparedStatement或CallableStatement,默認值為PREPARED。這個相信大多數朋友自己寫JDBC的時候也只用過PreparedStatement
  • resultSetType—-FORWARD_ONLY、SCROLL_SENSITIVE、SCROLL_INSENSITIVE中的一種,默認不設置(即由驅動自行處理)

xml寫完了,看一下如何寫Java程序,比較簡單,使用selectList方法即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public List<Student> selectStudentsById(int studentId)
{
????SqlSession ss = ssf.openSession();
????List<Student> list = null;
????try
????{
????????list = ss.selectList("com.xrq.StudentMapper.selectAll", studentId);
????}
????finally
????{
????????ss.close();
????}
????return list;
}

同樣,結果也就不演示了,查出來和數據庫內的數據相符。

3、使用resultMap來接收查詢結果

上面使用的是resultType來接收查詢結果,下面來看另外一種方式—-使用resultMap,被MyBatis稱為MyBatis中最重要最強大的元素。

上面使用resultType的方式是有前提的,那就是假定列名和Java Bean中的屬性名存在對應關系,如果名稱不對應,也沒關系,可以采用類似下面的方式:

1
2
3
4
5
6
7
<select id="selectAll" parameterType="int" resultType="Student" flushCache="false" useCache="true"
????timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
????<![CDATA[
????????select student_id as "studentId",student_name as "studentName",student_age as "studentAge",
      student_phone as "studentPhone" from student whehre restudentId > #{id};
????]]>
</select>

毫無疑問,這樣很繁瑣,我們可以采用resultMap來解決列名不匹配的問題,把2由resultType的形式改成resultMap的形式,Java代碼不需要動:

1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap type="Student" id="studentResultMap">
????<id property="studentId" column="studentId" />
????<result property="studentName" column="studentName" />
????<result property="studentAge" column="studentAge" />
????<result property="studentPhone" column="studentPhone" />
</resultMap>
<select id="selectAll" parameterType="int" resultMap="studentResultMap" flushCache="false" useCache="true"
????????timeout="10000" fetchSize="100" statementType="PREPARED" resultSetType="FORWARD_ONLY">
????<![CDATA[
????????select * from student where studentId > #{id};
????]]>
</select>

這樣就可以了,注意兩點:

1、resultMap定義中主鍵要使用id

2、resultMap和resultType不可以同時使用

對resultMap有很好的理解的話,許多復雜的映射問題就很好解決了。

insert

select看完了,接著看一下插入的方法。首先是student.xml的配置方法,由于插入數據涉及一個主鍵問題,我用的是MySQL,我試了一下使用以下兩種方式都可以:

1
2
3
4
5
<insert id="insertOneStudent" parameterType="Student">
????<![CDATA[
????????insert into student??? values(null, #{studentName}, #{studentAge}, #{studentPhone});
????]]>???
</insert>
1
2
3
4
5
6
<insert id="insertOneStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="studentId">
????<![CDATA[
????????insert into student(studentName, studentAge, studentPhone)
????????????values(#{studentName}, #{studentAge}, #{studentPhone});
????]]>???
</insert>

前一種是MySQL本身的語法,主鍵字段在insert的時候傳入null,后者是MyBatis支持的生成主鍵方式,useGeneratedKeys表示讓數據庫自動生成主鍵,keyProperty表示生成主鍵的列。

Java代碼比較容易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void insertOneStudent(String studentName, int studentAge, String studentPhone)
{
????SqlSession ss = ssf.openSession();
????try
????{
????????ss.insert("com.xrq.StudentMapper.insertOneStudent",
????????????new Student(0, studentName, studentAge, studentPhone));
????????ss.commit();
????}
????catch (Exception e)
????{
????????ss.rollback();
????}
????finally
????{
????????ss.close();
????}
}

還是一樣,insert方法比如傳入Student的實體類,如果insertOneStudent方法要傳入的參數比較多的話,建議不要把每個屬性單獨作為形參,而是直接傳入一個Student對象,這樣也比較符合面向對象的編程思想。

然后還有一個問題,這個我回頭還得再看一下。照理說設置了transactionManager的type為JDBC,對事物的處理應該和底層JDBC是一致的,JDBC默認事物是自動提交的,這里事物卻得手動提交,拋異常了得手動回滾才行。

修改、刪除元素

修改和刪除元素比較類似,就看一下student.xml文件怎么寫,Java代碼就不列了,首先是修改元素:

1
2
3
4
5
6
<update id="updateStudentAgeById" parameterType="Student">
????<![CDATA[
????????update student set studentAge = #{studentAge} where
????????????studentId = #{studentId};
????]]>???
</update>

接著是刪除元素:

1
2
3
4
5
<delete id="deleteStudentById" parameterType="int">
????<![CDATA[
????????delete from student where studentId = #{studentId};
????]]>???
</delete>

這里我又發現一個問題,記錄一下,update的時候Java代碼是這么寫的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void updateStudentAgeById(int studentId, int studentAge)
{
????SqlSession ss = ssf.openSession();
????try
????{
????????ss.update("com.xrq.StudentMapper.updateStudentAgeById",
????????????????new Student(studentId, null, studentAge, null));
????????ss.commit();
????}
????catch (Exception e)
????{
????????ss.rollback();
????}
????finally
????{
????????ss.close();
????}
}

studentId和studentAge必須是這個順序,互換位置就更新不了學生的年齡了,這個是為什么我還要后面去研究一下,也可能是寫代碼的問題。?

SQL

SQL可以用來定義可重用的SQL代碼段,可以包含在其他語句中,比如我把上面的插入換一下,先定義一個SQL:

1
2
3
<sql id="insertColumns">
????studentName, studentAge, studentPhone
</sql>

然后在修改一下insert:

1
2
3
4
<insert id="insertOneStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="studentId">
????insert into student(<include refid="insertColumns" />)
????????values(#{studentName}, #{studentAge}, #{studentPhone});
</insert>

注意這里要把”<![CDATA[ ... ]]>”給去掉,否則”<”和”>”就被當成SQL里面的小于和大于了,因此使用SQL的寫法有一定限制,使用前要注意一下避免出錯。

轉載于:https://www.cnblogs.com/tuojunjie/p/6210282.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/138998.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息