1. 程式人生 > >java mybatis學習之$和#區別,mapper代理接口,動態SQL,在日誌中輸出mybatis的sql語句

java mybatis學習之$和#區別,mapper代理接口,動態SQL,在日誌中輸出mybatis的sql語句

except 控制臺 處理方式 ron target 技術分享 需要 prefix job

1.在mybatis中,$和#的區別:

#{}:表示一個預處理參數,參數類型不定,是根據傳入的參數類型來設定的。類似於JDBC中的?

特例使用,模糊查詢:(針對oracle):

and username like concat(concat(‘%‘,#{username}),‘%‘)

${}:相當於是我們的JDBC裏的字符串拼接。這裏就相當於傳入的就是一個字符串(不管傳入什麽樣的數據類型,都是字符串)

and username like ‘%${value}%‘

2.$和#在mybatis中的優缺點:

#{}:傳進去的值可以設置其數據類型。會根據傳入的數據類型自動加字符串的單引號或者不加單引號。預處理參數。可以防止SQL註入。

${}:采取的$的方式傳入參數,所有采取$的方式傳入的參數都只是字符串(無論傳入的是什麽,都會當成字符串處理),潛在的危險就是SQL註入的問題。而${}的優勢在於字符串的拼接可以處理sql語句的一些特殊情況,例如:

默認情況下,使用#{}格式的語法會導致 MyBatis 創建預處理語句屬性並以它為背景設 置安全的值(比如?) 。這樣做很安全,很迅速也是首選做法,有時你只是想直接在 SQL 語 句中插入一個不改變的字符串。比如,像 ORDER BY,你可以這樣來使用:

ORDER BY ${columnName}

3.mapper代理接口和動態SQL

直接上代碼:

這是測試的代碼

 1 package
com.jinglin.hotelsup.test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.List; 6 7 import org.apache.ibatis.io.Resources; 8 import org.apache.ibatis.session.SqlSession; 9 import org.apache.ibatis.session.SqlSessionFactory; 10 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
11 import org.apache.log4j.Logger; 12 import org.junit.Test; 13 14 import com.jinglin.hotelsup.dao.impl.UserInfoDao; 15 import com.jinglin.hotelsup.mapper.UserInfoMapper; 16 import com.jinglin.hotelsup.model.UserInfo; 17 18 public class TestUserInfo { 19 20 //日誌 21 static Logger logger = Logger.getLogger(TestUserInfo.class); 22 //定義一個工廠 23 static SqlSessionFactory factory = null; 24 //靜態塊 25 static{ 26 InputStream inputStream = null; 27 try { 28 inputStream = Resources.getResourceAsStream("mybatis-config.xml"); 29 } catch (IOException e) { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 logger.error("創建連接對象出錯,錯誤信息:"+e.getMessage()); 33 } 34 factory = new SqlSessionFactoryBuilder().build(inputStream); 35 } 36 37 @Test 38 public void test(){ 39 SqlSession session = factory.openSession(); 40 //代理的接口 41 UserInfoMapper userInfoMapper = session.getMapper(UserInfoMapper.class); 42 //添加 43 /*UserInfo userInfo = new UserInfo(); 44 userInfo.setUsername("張三3號"); 45 //userInfo.setUserpwd("123456"); 46 userInfo.setJob("程序員"); 47 //userInfo.setCard("521354499653159894"); 48 int count = userInfoMapper.insertItem(userInfo); 49 //事務提交 50 session.commit(); 51 //關閉 52 session.close(); 53 System.out.println("受影響行數:"+count); 54 System.out.println("剛剛插入的主鍵:"+userInfo.getUserid());*/ 55 56 //修改 57 /*UserInfo userInfo = new UserInfo(); 58 userInfo.setUsername("張三1號"); 59 userInfo.setUserpwd("123456"); 60 // userInfo.setJob("程序員"); 61 // userInfo.setCard("521354499653159894"); 62 userInfo.setUserid(21); 63 int count = userInfoMapper.updateItem(userInfo); 64 //事務提交 65 session.commit(); 66 //關閉 67 session.close(); 68 System.out.println("受影響行數:"+count);*/ 69 70 //邏輯刪除 71 /*UserInfo userInfo = new UserInfo(); 72 userInfo.setUserid(21); 73 int count = userInfoMapper.deleteItem(userInfo); 74 //事務提交 75 session.commit(); 76 //關閉 77 session.close(); 78 System.out.println("受影響行數:"+count);*/ 79 80 //查詢多條 81 /*UserInfo userInfo = new UserInfo(); 82 //userInfo.setUsername("張三"); 83 List<UserInfo> list = userInfoMapper.selectAll(userInfo); 84 for (UserInfo u : list) { 85 System.out.println(u.getUsername()+"\t"+u.getUserpwd()); 86 }*/ 87 88 //模糊查詢 89 /*UserInfo userInfo = new UserInfo(); 90 userInfo.setUsername("張"); 91 List<UserInfo> list = userInfoMapper.getDimList(userInfo); 92 for (UserInfo u : list) { 93 System.out.println(u.getUsername()+"\t"+u.getUserpwd()); 94 }*/ 95 } 96 }

mybatis-config配置

<?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>
    <settings>
        <!-- 通過日誌記錄顯示mybatis的執行過程 -->
        <setting name="logImpl" value="LOG4J"/>
     </settings>
    <!-- 定義別名 -->
    <typeAliases>
        <typeAlias type="com.jinglin.hotelsup.model.UserInfo" alias="UserInfo"/>
    </typeAliases>
    <!-- 定義多個配置環境 -->
    <environments default="development">
        <!-- 定義其中一個配置環境 -->
        <environment id="development">
            <!-- 定義事務處理類型 -->
            <transactionManager type="JDBC"/>
            <!-- 定義數據源 -->
            <dataSource type="POOLED">
            <!-- 數據庫驅動名稱 -->
            <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
               <!-- 數據庫URL地址 -->
               <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
               <!-- 數據賬號 -->
               <property name="username" value="hotelmanager"/>
               <!-- 數據庫密碼 -->
               <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
    <mapper resource="com/jinglin/hotelsup/mapper/UserInfoMapper.xml"/>
    </mappers>
</configuration>

dao層的接口

package com.jinglin.hotelsup.dao;

import java.util.List;

public interface IDaoHotelsup<T> {
    public int insertItem(T t);//添加一條數據
    public int updateItem(T t);//修改一條數據
    public T selectById(T t);//根據id查詢單條
    public int deleteItem(T t);//刪除一條數據
    public List<T> selectAll(T t);//查詢全部
    public List<T> getDimList(T t);//模糊查詢
}

mapper接口,在這裏是接口繼承了接口

package com.jinglin.hotelsup.mapper;

import com.jinglin.hotelsup.dao.IDaoHotelsup;
import com.jinglin.hotelsup.model.UserInfo;

public interface UserInfoMapper extends IDaoHotelsup<UserInfo>{

}

mapper的映射

<?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.jinglin.hotelsup.mapper.UserInfoMapper">
    <!-- 定義重復使用的代碼段 -->
    <sql id="selectColumns">
        select * from userinfo where del=‘N‘ 
    </sql>
    <!-- 添加 -->
    <insert id="insertItem" parameterType="UserInfo" useGeneratedKeys="true" keyColumn="userid" keyProperty="userid">
       insert into userinfo
       <trim prefix="(userid," suffixOverrides=",">
         <if test="username != null">
             username,
         </if>
         <if test="userpwd != null">
             userpwd,
         </if>
         <if test="card != null">
             card,
         </if>
         <if test="job != null">
             job
         </if>
       </trim>
       <trim prefix=")values(usersid.nextval,">
         <if test="username != null">
                #{username},
         </if>
         <if test="userpwd != null">
             #{userpwd},
         </if>
         <if test="card != null">
             #{card},
         </if>
         <if test="job != null">
             #{job}
         </if>
       </trim>
      )
    </insert>
    
    <!-- 修改 -->
    <update id="updateItem" parameterType="UserInfo">
        update userinfo 
        <set>
          <if test="username != null">
            username=#{username},
          </if>
          <if test="userpwd != null">
            userpwd=#{userpwd},
          </if>
          <if test="card != null">
              card=#{card},
          </if>
          <if test="job != null">
              job=#{job}
          </if>
        </set>
        where userid=#{userid}
    </update>
    
    <!-- 刪除 -->
    <delete id="deleteItem" parameterType="UserInfo">
        <!-- delete from userinfo where userid=#{userid} -->
        <!-- 邏輯刪除 -->
        update userinfo set del=‘Y‘ where userid=#{userid}
    </delete>
    
    <!-- 根據id查詢 -->
    <select id="selectById" resultType="UserInfo" parameterType="UserInfo">
           <include refid="selectColumns"></include>
           and userid=#{userid}
    </select>
    
    <!-- 查詢全部 -->
    <select id="selectAll" resultType="UserInfo">
           <include refid="selectColumns"></include>
           <if test="username != null and username != ‘‘">
           and username=#{username}
           </if>
           <if test="userpwd != null and userpwd != ‘‘">
        and userpwd=#{userpwd}
        </if>
           <if test="card != null and card != ‘‘">
        and card=#{card}
        </if>
           <if test="job != null and job != ‘‘">
        and job=#{job}
        </if>
    </select>
    
    <!-- 迷糊查詢-->
    <select id="getDimList" resultType="UserInfo" parameterType="UserInfo">
        <include refid="selectColumns"></include>
        <if test="username != null and username != ‘‘">
           and username like concat(concat(‘%‘,#{username}),‘%‘)
           </if>
           <if test="userpwd != null and userpwd != ‘‘">
        and userpwd like concat(concat(‘%‘,#{userpwd}),‘%‘)
        </if>
           <if test="card != null and card != ‘‘">
        and card like concat(concat(‘%‘,#{card}),‘%‘)
        </if>
           <if test="job != null and job != ‘‘">
        and job like concat(concat(‘%‘,#{job}),‘%‘)
        </if>
    </select>
</mapper>

以下是日誌文件顯示SQL語句

首先,註意mybatis-config中的log4j的<setting>標簽

<settings>
        <!-- 通過日誌記錄顯示mybatis的執行過程 -->
        <setting name="logImpl" value="LOG4J"/>
     </settings>

其次,是log4j部分

#將ibatis log4j運行級別調到DEBUG可以在控制臺打印出ibatis運行的sql語句
log4j.rootLogger=debug,stdout,logfile
### 把日誌信息輸出到控制臺 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
### 把日誌信息輸出到文件:jbit.log ###
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=F:/logs/test.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
###顯示SQL語句部分
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

顯示SQL語句部分則是實現將sql語句顯示出來的關鍵,實現結果如下

技術分享

接下來是動態SQL的詳解,

動態SQL,顧名思義就是將SQL語句動態化,區別與傳統的SQL語句。動態SQL語句的優勢在於它的靈活,在開發項目的過程中可以減少代碼量

MyBatis 的一個強大的特性之一通常是它的動態 SQL 能力。 如果你有使用 JDBC 或其他 相似框架的經驗,你就明白條件地串聯 SQL 字符串在一起是多麽的痛苦,確保不能忘了空 格或在列表的最後省略逗號。動態 SQL 可以徹底處理這種痛苦。

if

在動態 SQL 中所做的最通用的事情是包含部分 where 字句的條件。比如:

<select id="findActiveBlogWithTitleLike" 
     parameterType="Blog" resultType="Blog">
  SELECT * FROM BLOG 
  WHERE state = ‘ACTIVE’ 
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

這條語句會提供一個可選的文本查找功能。如果你沒有傳遞 title,那麽所有激活的博客 都會被返回。但是如果你傳遞了 title,那麽就會查找相近的 title(對於敏銳的檢索,這中情 況下你的參數值需要包含任意的遮掩或通配符)的博客。

假若我們想可選地搜索 title 和 author 呢?首先,要改變語句的名稱讓它有意義。然後 簡單加入另外的一個條件。

<select id="findActiveBlogLike" 
     parameterType="Blog" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

choose, when, otherwise

有時我們不想應用所有的條件, 相反我們想選擇很多情況下的一種。 Java 中的 switch 和 語句相似,MyBatis 提供 choose 元素。

我們使用上面的示例,但是現在我們來搜索當 title 提供時僅有 title 條件,當 author 提 供時僅有 author 條件。如果二者都沒提供,只返回 featured blogs(也許是由管理員策略地選 擇的結果列表,而不是返回大量沒有意義的隨機博客結果列表)。

<select id="findActiveBlogLike" 
     parameterType="Blog" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim, where, set

前面的例子已經方便地處理了一個臭名昭著的動態 SQL 問題。要考慮我們回到“if”示 例後會發生什麽,但是這次我們將“ACTIVE = 1”也設置成動態的條件。

<select id="findActiveBlogLike" 
     parameterType="Blog" resultType="Blog">
  SELECT * FROM BLOG 
  WHERE 
  <if test="state != null">
    state = #{state}
  </if> 
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

如果這些條件都沒有匹配上將會發生什麽?這條 SQL 結束時就會成這樣:

SELECT * FROM BLOG 
WHERE

這會導致查詢失敗。如果僅僅第二個條件匹配是什麽樣的?這條 SQL 結束時就會是這 樣:

SELECT * FROM BLOG 
WHERE 
AND title like someTitle

這個查詢也會失敗。這個問題不能簡單的用條件來解決,如果你從來沒有這樣寫過,那 麽你以後也不會這樣來寫。

MyBatis 有一個簡單的處理,這在 90%的情況下都會有用。而在不能使用的地方,你可 以自定義處理方式。加上一個簡單的改變,所有事情都會順利進行:

<select id="findActiveBlogLike" 
     parameterType="Blog" resultType="Blog">
  SELECT * FROM BLOG 
  <where> 
    <if test="state != null">
         state = #{state}
    </if> 
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where 元素知道如果由被包含的標記返回任意內容,就僅僅插入“WHERE” 。而且,如 果以“AND”或“OR”開頭的內容,那麽就會跳過 WHERE 不插入。

如果 where 元素沒有做出你想要的,你可以使用 trim 元素來自定義。比如,和 where 元素相等的 trim 元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>

prefixOverrides 屬性采用管道文本分隔符來覆蓋, 這裏的空白也是重要的。 它的結果就是移除 在 prefixOverrides 屬性中指定的內容,插入在 with 屬性中的內容。

和動態更新語句相似的解決方案是 set。set 元素可以被用於動態包含更新的列,而不包 含不需更新的。比如:

<update id="updateAuthorIfNecessary"
       parameterType="domain.blog.Author">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

這裏,set 元素會動態前置 SET 關鍵字,而且也會消除任意無關的逗號,那也許在應用 條件之後來跟蹤定義的值。

如果你對和這相等的 trim 元素好奇,它看起來就是這樣的:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

註意這種情況下我們覆蓋一個後綴,而同時也附加前綴。

java mybatis學習之$和#區別,mapper代理接口,動態SQL,在日誌中輸出mybatis的sql語句