MyBatis(4)動態SQL
MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記添加必要的空格,還要註意去掉列表最後一個列名的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。
雖然在以前使用動態 SQL 並非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強大的動態 SQL 語言得以改進這種情形。
動態 SQL 元素和 JSTL 或基於類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間了解。MyBatis 3 大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis 采用功能強大的基於 OGNL 的表達式來淘汰其它大部分元素。
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties" ></properties> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="DynamicSQL.xml"/> </mappers> </configuration>
Employee.java(getter&setter&toString)
public class Employee { private int id; private String name; private String gender; private String email; private Department dept; }
現在基本的布局已經完成!!
1)if
A.在DynamicMapper.java接口中
//攜帶了哪個字段的查詢條件就攜帶這個字段的值 public List<Employee> getEmpByIf(Employee emp);
在Dynamic‘SQl.xml文件
<!-- if --> <!-- 查詢員工,要求,攜帶了那個字段查詢條件就帶上那個字段的字段值 --> <!-- public List<Employee> getEmpByIf(Employee emp); --> <select id="getEmpByIf" resultType="com.MrChengs.bean.Employee"> select * from test where <!-- test:判斷表達式(OGNL) --> <!-- OGNL:apache官方文檔有明確的解釋說明 --> <!-- 從參數中取值進行判斷不是數據庫中取值 --> <!-- 特殊字符應該寫轉義字符 --> <if test="id!=null"> id=#{id} </if> <if test="name!=null and name!=‘‘"> and name like #{name} </if> <if test="email!=null and email.trim()!=‘‘"> and email like #{email} </if> </select>在這個文件的內容簡要的進行說明一下: and name like #{name} 這裏的紅色的name是我們查詢的name值,不是數據庫中的name #{name}是把我們手動輸入的紅色name傳遞過去,進行數據庫的查詢 測試類:
public SqlSessionFactory getSqlSessionFactory() throws IOException{ String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); return new SqlSessionFactoryBuilder().build(inputStream); } @Test public void test() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); //傳入的紅色name值進行數據庫的查詢 Employee emp = new Employee(5, "%Mr%", "boy", "%1287%"); List<Employee> emps = mapper.getEmpByIf(emp); System.out.println(emps); }finally{ session.close(); } }
查詢之後的顯示代碼
DEBUG 10-02 12:13:49,806 ==> Preparing: select * from test where id=? and name like ? and email like ? (BaseJdbcLogger.java:159) DEBUG 10-02 12:13:49,843 ==> Parameters: 5(Integer), %Mr%(String), %1287%(String) (BaseJdbcLogger.java:159) DEBUG 10-02 12:13:49,873 <== Total: 1 (BaseJdbcLogger.java:159) [Employee [id=5, name=MrChengs, gender=boy, [email protected], dept=null]]
B.在查詢的時候,如果某些時候某些條件沒帶可能導致sql拼裝有問題 實例:
<select id="getEmpByIf" resultType="com.MrChengs.bean.Employee"> select * from test where <!-- test:判斷表達式(OGNL) --> <!-- OGNL:apache官方文檔有明確的解釋說明 --> <!-- 從參數中取值進行判斷不是數據庫中取值 --> <!-- 特殊字符應該寫轉義字符 --> <!--此時我們假設忘記把id傳進來 --> <if test="name!=null and name!=‘‘"> and name like #{name} </if> <if test="email!=null and email.trim()!=‘‘"> and email like #{email} </if> </select>
look:
show message:DEBUG 10-02 12:18:30,831 ==> Preparing: select * from test where and name like ? and email like ?
(BaseJdbcLogger.java:159)
solution ①: where 1=1
<select id="getEmpByIf" resultType="com.MrChengs.bean.Employee"> select * from test <!-- 加入固定的條件,怎麽拼裝都行 --> where 1=1 <!-- test:判斷表達式(OGNL) --> <!-- OGNL:apache官方文檔有明確的解釋說明 --> <!-- 從參數中取值進行判斷不是數據庫中取值 --> <!-- 特殊字符應該寫轉義字符 --> <if test="name!=null and name!=‘‘"> and name like #{name} </if> <if test="email!=null and email.trim()!=‘‘"> and email like #{email} </if> </select>
solution ②:使用<where></where> 只會去掉一個and 或者or
<select id="getEmpByIf" resultType="com.MrChengs.bean.Employee"> select * from test <where> <!-- test:判斷表達式(OGNL) --> <!-- OGNL:apache官方文檔有明確的解釋說明 --> <!-- 從參數中取值進行判斷不是數據庫中取值 --> <!-- 特殊字符應該寫轉義字符 --> <if test="name!=null and name!=‘‘"> and name like #{name} </if> <if test="email!=null and email.trim()!=‘‘"> and email like #{email} </if> </where> </select>
註意使用and
2.使用trim標簽進行,字符串截取
先看一個案例的錯誤代碼展示: DynamicSQLMapper.java//測試Trim public List<Employee> getEmpByIfTrim(Employee emp);
在DynamicSQL.xml
<!-- 測試Trim() --> <!-- public List<Employee> getEmpByIfTrim(Employee emp); --> <select id="getEmpByIfTrim" resultType="com.MrChengs.bean.Employee"> select * from test where <if test="id!=null"> id=#{id} and </if> <if test="name!=null and name!=‘‘"> name like #{name} and </if> <if test="email!=null and email.trim()!=‘‘"> email like #{email} </if> </select>
假設我們此時傳參為name屬性一個
@Test public void testgetEmpByIfTrim() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); Employee emp = new Employee("%Mr%", null, null); List<Employee> emps = mapper.getEmpByIfTrim(emp); System.out.println(emps); }finally{ session.close(); } }
拼串結果
DEBUG 10-02 13:31:59,995 ==> Preparing: select * from test where id=? and name like ? and
開始使用trim標簽:(一些用法都在註釋中,請註意看註釋)
<!-- 測試Trim() --> <!-- public List<Employee> getEmpByIfTrim(Employee emp); --> <select id="getEmpByIfTrim" resultType="com.MrChengs.bean.Employee"> select * from test
<!-- prefix:前綴, trim標簽體中是整個字符串拼串後的結果 給拼串後的整體字符串加一個前綴--> <!-- prefixOverrides:前綴覆蓋, 去點整個前綴前面多余的字符串 --> <!-- suffix:後綴, 給拼串後的整個字符串加一個後綴 --> <!-- suffixOverrides:後綴覆蓋,去掉整個字符串後面多余的字符串 -->
<trim prefix="where" suffixOverrides="and"> <if test="name!=null and name!=‘‘"> name like #{name} and </if> <if test="email!=null and email.trim()!=‘‘"> email like #{email} and </if> <if test="gender!=null"> gender=#{gender} </if> </trim> </select>
測試:
public void testgetEmpByIfTrim() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); Employee emp = new Employee("%Mr%", null, null); List<Employee> emps = mapper.getEmpByIfTrim(emp); System.out.println(emps); }finally{ session.close(); } }
結果拼串:
DEBUG 10-02 13:43:25,216 ==> Preparing: select * from test where name like ? (BaseJdbcLogger.java:159) DEBUG 10-02 13:43:25,266 ==> Parameters: %Mr%(String) (BaseJdbcLogger.java:159)
註意:在測試id的時候,不寫則默認為零,博主自己測試的時候遇到的,所以把id的查詢條件拿掉了!
3.choose分支選擇 如果帶了id使用id進行查詢,帶了name就是用name進行查詢 只能使用一個進行查詢 接口類的代碼://測試choose public List<Employee> getEmpBychoose(Employee emp);
DynamicSQL.xml:
<!-- choose --> <!-- 如果帶了id使用id進行查詢,帶了name就是用name進行查詢,只能使用一個進行查詢 --> <!-- public List<Employee> getEmpBychoose(Employee emp); --> <select id="getEmpBychoose" resultType="com.MrChengs.bean.Employee"> select * from test <where> <choose> <when test="name!=null"> name like #{name} </when> <when test="email!=null"> email = #{email} </when> <when test="id!=null"> id=#{id} </when> <otherwise> d_id=1 </otherwise> </choose> </where> </select>
測試代碼:
//測試choose @Test public void testgetEmpBychoose() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); Employee emp = new Employee("%Mr%", null, null); emp.setId(5); List<Employee> emps = mapper.getEmpBychoose(emp); System.out.println(emps); }finally{ session.close(); } }
結果:
DEBUG 10-02 14:07:35,311 ==> Preparing: select * from test WHERE name like ? (BaseJdbcLogger.java:159)
DEBUG 10-02 14:07:35,363 ==> Parameters: %Mr%(String) (BaseJdbcLogger.java:159)
此時我們不僅傳入了name同時還傳入了id,但是拼串之後是使用name進行查詢的
3.更新
A.<set></set>版本 在接口中://更新方法 public void updataEmp(Employee emp);
在DynamicSQl.xml文件:
<!-- update更新 --> <!-- 更新 --> <!-- public void updataEmp(Employee emp); --> <update id="updataEmp"> update test <set> <if test="name!=null">name=#{name},</if> <if test="email!=null"> email=#{email},</if> <if test="gender!=null">gender=#{gender},</if> </set> where id=#{id} </update>
使用<set>標簽,可以自動為我們解決存在的 ”,“ 問題
測試://更新upddate @Test public void testgetEmpupdate() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); Employee emp = new Employee("MrChengsR", "gril", null); emp.setId(7); mapper.updataEmp(emp); System.out.println(emp); session.commit(); }finally{ session.close(); } }
此時修改數據成功
B.<trim><trim> version 僅僅是修改xml文件,其余的都不變<update id="updataEmp"> update test <trim prefix="set" suffixOverrides=","> <if test="name!=null">name=#{name},</if> <if test="email!=null"> email=#{email},</if> <if test="gender!=null">gender=#{gender},</if> </trim> where id=#{id} </update>
4.foreach
A)foreach: DynamicSQLMapper.java//foreach public List<Employee> getEmpsByCollection(List<Integer> list);
DynamicSQL.xml
<!-- foreach: --> <!-- public List<Employee> getEmpsByCollection(Employee emp); --> <select id="getEmpsByCollection" resultType="com.MrChengs.bean.Employee" > select * from test where id in( <!-- collection:指定遍歷的集合 --> <!-- list類型的參數會做特殊的處理封裝在map中,map的key叫list --> <!-- item:將當前遍歷出的元素賦值給指定的變量 --> <!-- #{變量名} 就能取出當前遍歷的元素 --> <!-- separator:每個元素之間的分隔符 此時是in(a,b,c,d)這裏面的 , --> <!-- open:遍歷出所有結果拼接一個開始的字符 --> <!-- close:便利的所有結果拼出結尾 --> <!-- index:遍歷list是索引,遍歷map就是map的key --> <foreach collection="list" item="item_id" separator=","> #{item_id} </foreach> ) </select>
測試類:
@Test public void testgetEmpForeach() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); List<Employee> emps = mapper.getEmpsByCollection(Arrays.asList(5,7,8)); for(Employee emp : emps){ System.out.println(emp); } }finally{ session.close(); } }
得到結果:
DEBUG 10-02 19:16:01,838 ==> Preparing: select * from test where id in( ? , ? , ? ) (BaseJdbcLogger.java:159) DEBUG 10-02 19:16:01,887 ==> Parameters: 5(Integer), 7(Integer), 8(Integer) (BaseJdbcLogger.java:159) DEBUG 10-02 19:16:01,909 <== Total: 3 (BaseJdbcLogger.java:159) Employee [id=5, name=MrChengs, gender=boy, [email protected], dept=null] Employee [id=7, name=MrChengs, gender=gril, [email protected], dept=null] Employee [id=8, name=MrChen, gender=gril, [email protected], dept=null]
B.批量保存
方法1: 接口類中://批量存取 public void addEmps(@Param("emps")List<Employee> employee);
xml文件:
<!-- //批量存取--> <!-- public void addEmps(@Param("emps")Employee employee); --> <insert id="addEmps"> insert into test(name,gender,email,d_id) values <foreach collection="emps" separator="," item="emp"> <!-- 傳參數之前是我們new的一個對象,傳參數之後是插入數據庫的數據 --> (#{emp.name},#{emp.gender},#{emp.email},#{emp.dept.id}) </foreach> </insert>
實現類:
//批量存取 @Test public void testgetEmpaddEmps() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); List<Employee> employee = new ArrayList<Employee>(); employee.add(new Employee("Ma", "gril", "Ma@Ma", new Department(1))); employee.add(new Employee("Mb", "boy", "Mb@Mb", new Department(2))); mapper.addEmps(employee); session.commit(); }finally{ session.close(); } }此時是成功插入數據 方法二:
<!-- 方法二 --> <!-- 需要加上 --> <!-- jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true --> <insert id="addEmps"> <foreach collection="emps" separator=";" item="emp"> insert into test(name,gender,email,d_id) values (#{emp.name},#{emp.gender},#{emp.email},#{emp.dept.id}) </foreach> </insert>
其余不變可以進行測試
c.兩個重要的參數 <!-- 兩個重要的參數 --> <!-- _parameter:代表整個參數,單個參數就是這個參數,多個參數就是封裝成的map --> <!-- _databaseId:配置了databaseIdProvider標簽,就是代表當前數據庫的別名 --> _databaseId: mybatis-config.xml<databaseIdProvider type="DB_VENDOR"> <property name="MySQL" value="mysql"/> <property name="Oracle" value="oracle"/> </databaseIdProvider>
接口類中
//測試兩個屬性 public List<Employee> getEmpselect();
DynamicMapper.xml
<!-- 兩個重要的參數 --> <!-- _parameter:代表整個參數,單個參數就是這個參數,多個參數就是封裝成的map --> <!-- _databaseId:配置了databaseIdProvider標簽,就是代表當前數據庫的別名 --> <!-- public Employee getEmpselect(int id); --> <!-- 修改if中的test條件即可實現不同數據庫之間的查詢 --> <select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql"> <if test="_databaseId==‘mysql‘"> select * from test </if> <if test="_databaseId==‘oracle‘"> select * from test </if> </select>
測試類:
//兩個重要的參數 @Test public void testgetEmpselect() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); List<Employee> emps= mapper.getEmpselect(); for(Employee emp : emps){ System.out.println(); } }finally{ session.close(); } }
此時可以成功查詢數據!! _parameter 在接口類中:把剛剛測試代碼加上id
//測試兩個屬性 public List<Employee> getEmpselect(int id);
在xnl文件中:
<!-- public Employee getEmpselect(int id); --> <select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql"> <if test="_databaseId==‘mysql‘"> select * from test <if test="_parameter!=null"> where id=#{id} </if> </if> <if test="_databaseId==‘oracle‘"> select * from test </if> </select>
測試類:
@Test public void testgetEmpselect() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); List<Employee> emps= mapper.getEmpselect(5); System.out.println(emps); }finally{ session.close(); } }此時的查詢成功!!! D.bind標簽的使用 接口類中:
//測試兩個屬性 //public List<Employee> getEmpselect(); //public List<Employee> getEmpselect(int id); public List<Employee> getEmpselect(Employee em);
xml文件:<select id="getEmpselect" resultType="com.MrChengs.bean.Employee" databaseId="mysql"> <!-- bind:可以將OGNL表達式的值綁定到一個變量中,方便引用這個變量的值 --> <!-- name :是我們指定的綁定參數--> <!-- value :指定參數的值 --> <bind name="_name" value="‘%‘+name+‘%‘"/> <if test="_databaseId==‘mysql‘"> select * from test <if test="_parameter!=null"> where name like #{_name} </if> </if> <if test="_databaseId==‘oracle‘"> select * from test </if> </select>
測試類:
@Test public void testgetEmpselect() throws IOException { SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession session = sessionFactory.openSession(); try{ DynamicSQLMapper mapper = session.getMapper(DynamicSQLMapper.class); //List<Employee> emps= mapper.getEmpselect(); //List<Employee> emps= mapper.getEmpselect(5); Employee emp = new Employee(); emp.setName("M"); List<Employee> emps= mapper.getEmpselect(emp); System.out.println(emps); }finally{ session.close(); } }
E.SQL標簽
<!-- <include refid=""></include> --> <!-- SQL:抽取可重用的sql字段,方便後面引用 --> <!-- include:就是引用外部標簽 --> <!-- 1.sql抽取:經常要查詢的列名,或者插入用的列名抽取出來方便引用 2.include來引用已經抽取的sql 3.include還可以自定義一些property,sql標簽內部只能使用自定義的屬性 include-property:取值正確方式 ${prop} #{不可以使用這種方式} --> <sql id=""> <!-- 同時這裏面還可以使用 if進行判斷 --> <if test=""></if> </sql>MyBatis(4)動態SQL