MyBatis?在使用上的注意事項(xiàng)及其辨析(最新最全整理)
1. MyBatis 的在使用上的注意事項(xiàng)及其辨析
2. 準(zhǔn)備工作
數(shù)據(jù)表結(jié)構(gòu)的設(shè)計(jì),數(shù)據(jù)表名為:t_car
t_car 表中的數(shù)據(jù)信息:
在pom.xml
文件當(dāng)中配置相關(guān)的依賴的 jar 包如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.rainbowsea</groupId> <artifactId>mybatis-005-crud-blog</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- mybatis 的依賴--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.10</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!-- 引入 logback的依賴,這個(gè)日志框架實(shí)現(xiàn)了slf4j 規(guī)范--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency> </dependencies> </project>
配置 logback 的配置文件,用于打印顯示,我們的日志信息,方便我們查看我們的運(yùn)行過(guò)程,效果。
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 控制臺(tái)輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!--mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志輸出級(jí)別,logback日志級(jí)別包括五個(gè):TRACE < DEBUG < INFO < WARN < ERROR --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
配置 MyBatis 的核心配置文件,
<?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> <!-- 起別名--> <typeAliases> <!-- 使用 <package> 還可以將這個(gè)包下的所有的類的全部自動(dòng)起別名,別名就是簡(jiǎn)名,不區(qū)分大小寫 --> <package name="com.rainbowsea.mybatis.pojo"/> </typeAliases> <environments default="mybatis"> <environment id="mybatis"> <!-- MANAGED 沒(méi)有用第三框架管理的話,都是會(huì)被提交的,沒(méi)有事務(wù)上的管理了 --> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="MySQL123"/> </dataSource> </environment> </environments> <mappers> <!-- 這里也是可以使用 package 包名掃描,但是同樣的:對(duì)應(yīng)接口路徑要一致,接口名一致--> <mapper resource="CarMapper.xml"></mapper> </mappers> </configuration>
對(duì)照 t_car 創(chuàng)建的ORM 映射的 Car 類
注意:在MyBatis 當(dāng)中對(duì)應(yīng)的ORM ,一般在框架里對(duì)應(yīng)的 Bean實(shí)體類,一定要實(shí)現(xiàn)該 set 和 get 方法以及無(wú)參數(shù)構(gòu)造方法,無(wú)法框架無(wú)法使用反射機(jī)制,進(jìn)行操作 。
建議用包裝類,這樣可以防止 Null的問(wèn)題,因?yàn)椋ê?jiǎn)單類型 int num = null ,是不可以賦值為 null)的編譯無(wú)法通過(guò)
package com.rainbowsea.mybatis.pojo; public class Car { // 數(shù)據(jù)庫(kù)表當(dāng)中的字段應(yīng)該和pojo類的屬性一一對(duì)應(yīng) // 建議使用包裝類,這樣可以防止null的問(wèn)題 private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String carType; public Car() { } public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) { this.id = id; this.carNum = carNum; this.brand = brand; this.guidePrice = guidePrice; this.produceTime = produceTime; this.carType = carType; } @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", catType='" + carType + '\'' + '}'; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCarNum() { return carNum; } public void setCarNum(String carNum) { this.carNum = carNum; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public Double getGuidePrice() { return guidePrice; } public void setGuidePrice(Double guidePrice) { this.guidePrice = guidePrice; } public String getProduceTime() { return produceTime; } public void setProduceTime(String produceTime) { this.produceTime = produceTime; } public String getcarType() { return carType; } public void setcarType(String catType) { this.carType = catType; } }
對(duì)應(yīng)操作實(shí)現(xiàn)CRUD(增刪改查)的接口(這里是:CarMapper接口),在MyBtis 當(dāng)中 ,關(guān)于 CRUD(增刪改查)操作的接口/實(shí)現(xiàn)類,都是 mapper
結(jié)尾的作為持久層,而在 MVC的三層架構(gòu)中,則是以 dao
為后綴作為CRUD(增刪改查)操作的接口/實(shí)現(xiàn)類。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; public interface CarMapper { /** * 新增 Car * @param car * @return */ int insert(Car car); /** * 根據(jù)id 刪除 Car * @param id * @return */ int deleteById(Long id); /** * 修改汽車信息 * @param car * @return */ int update(Car car); /** * 根據(jù)id查詢汽車信息 * @param id * @return */ Car selectById(Long id); /** * 獲取所有的汽車信息 * @return */ List<Car> selectAll(); }
獲取 Sqlsession 對(duì)象的工具類的編寫。
package com.rainbowsea.mybatis.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; public class SqlSessionUtil { // 工具類的構(gòu)造方法一般都是私有話化的 // 工具類中所有的方法都是靜態(tài)的,直接類名即可調(diào)用,不需要 new 對(duì)象 // 為了防止new對(duì)象,構(gòu)造方法私有化。 private SqlSessionUtil() { } private static SqlSessionFactory sessionFactory = null; // 靜態(tài)代碼塊,類加載時(shí)執(zhí)行 // SqlSessionUtil 工具類在進(jìn)行第一次加載的時(shí)候,解析mybatis-config.xml 文件,創(chuàng)建SqlSessionFactory對(duì)象。 static { // 獲取到 SqlSessionFactoryBuilder 對(duì)象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 獲取到SqlSessionFactory 對(duì)象 // SQlsessionFactory對(duì)象,一個(gè)SqlSessionFactory對(duì)應(yīng)一個(gè) environment, 一個(gè)environment通常是一個(gè)數(shù)據(jù)庫(kù) try { sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis"); } catch (IOException e) { throw new RuntimeException(e); } } /** * 獲取會(huì)話對(duì)象 * @return SqlSession */ public static SqlSession openSession() { // 獲取到 SqlSession 對(duì)象 SqlSession sqlSession = sessionFactory.openSession(); return sqlSession; } }
3. #{ } 與 ${ } 的區(qū)別和使用
- #{ } :先編譯 SQL 語(yǔ)句,再對(duì)占位符傳值,底層是 PrepareedStatement 實(shí)現(xiàn),可以防止 SQL 注入,比較常用
- ${ }:先進(jìn)行SQL語(yǔ)句的拼接,然后再對(duì)SQL語(yǔ)句進(jìn)行編譯,底層是 Statement 實(shí)現(xiàn),這種方式存在 SQL注入現(xiàn)象,SQL注入的風(fēng)險(xiǎn),簡(jiǎn)單的說(shuō)就是,直接將傳入的值拼接為了SQL語(yǔ)句,然后再執(zhí)行的)。只有在需要進(jìn)行SQL語(yǔ)句關(guān)鍵字拼接的情況下才會(huì)用到。
- 簡(jiǎn)單的說(shuō)一個(gè)區(qū)別就是:#{} 傳的值是帶有 '' 單引號(hào)的,而 ${} 傳的值是(直接就是值,沒(méi)有單引號(hào),或者是雙引號(hào),兩個(gè)都沒(méi)有)
檢驗(yàn)一下。
這里我們根據(jù)汽車的 car_type 查詢,為新能源汽車的。多條記錄
首先這里我們先使用 #{} ,傳的是帶有 '' 單引號(hào)的值 ——> '新能源'
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; /** * 封裝汽車相關(guān)信息的pojo類,普通的Java類 */ public interface CarMapper { /** * 根據(jù)汽車類型獲取汽車信息 * @param carType * @return */ List<Car> selectByCarType(String carType); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectByCarType" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = #{carType} </select> </mapper>
運(yùn)行Java程序:
查詢成功。
下面我們?cè)賹?shí)驗(yàn)用 ${} ,直接就是新能源的值,沒(méi)有單引號(hào),也沒(méi)有雙引號(hào),就是直接就是——》新能源的值。運(yùn)行結(jié)果如下:
比較上述#{}和 ?{} 的運(yùn)行結(jié)果:
${}
通過(guò)執(zhí)行可以清楚的看到,sql 語(yǔ)句中是帶有 ? 的,這個(gè) ? 就是大家在JDBC中所學(xué)的占位符,專門用來(lái)接收值的。
把“新能源”以 String 類型的值,傳遞給 ? ,加上 '' 單引號(hào),作為字符串傳入進(jìn)行,執(zhí)行SQL語(yǔ)句
這就是 #{},它會(huì)先進(jìn)行sql語(yǔ)句的預(yù)編譯,然后再給占位符傳值。
而 ${} 是直接將我們的 新能源 作為值,傳入給SQL語(yǔ)句的,注意 ${} 的方式是不會(huì)加單引號(hào)/雙引號(hào)的,而是作為值,直接拼接到 SQL語(yǔ)句當(dāng)中去了,但是,在我們這個(gè)查詢的SQL當(dāng)中,新能源就是必須要為字符串才行的,不然是無(wú)法執(zhí)行SQL語(yǔ)句,是無(wú)法識(shí)別出來(lái)的。
3.1 什么情況下必須使用 $
當(dāng)需要進(jìn)行 sql 語(yǔ)句關(guān)鍵字拼接的時(shí)候,簡(jiǎn)單的說(shuō)就是當(dāng)我們要使用 SQL語(yǔ)句當(dāng)中的關(guān)鍵字的時(shí)候,以及要傳的值,不要單引號(hào)/雙引號(hào)的值的時(shí)候,就必須使用${ }
需求:通過(guò)向sql語(yǔ)句中注入asc或desc關(guān)鍵字,來(lái)完成數(shù)據(jù)的升序或降序排列。
根據(jù)數(shù)據(jù)表中的 produce_time日期時(shí)間進(jìn)行排序。
因?yàn)檫@里我們使用到了 ASC / DESC 這兩者都是 SQL語(yǔ)句當(dāng)中的關(guān)鍵字,所以我們需要使用 ${} 的方式。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; /** * 封裝汽車相關(guān)信息的pojo類,普通的Java類 */ public interface CarMapper { /** * 查詢所有的汽車信息,然后通過(guò) asc 升序,desc 降序 * @param ascOrDesc * @return */ List<Car> selectAllByAscOrDesc(String ascOrDesc); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectAllByAscOrDesc" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car order by produce_time ${ascOrDesc} </select> </mapper>
Java程序運(yùn)行測(cè)試:
public class TestCarMapper { @Test public void testSelectAllByAscOrDesc() { SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper = sqlSession.getMapper(CarMapper.class); List<Car> cars = mapper.selectAllByAscOrDesc("asc"); cars.forEach(car -> { System.out.println(car); }); sqlSession.close(); } }
如果換成 #{} ,將SQL 語(yǔ)句的關(guān)鍵字 asc 作為字符串的形式拼接到 SQL語(yǔ)句當(dāng)中是會(huì)編譯失敗的了。
3.1.1 拼接表名
業(yè)務(wù)背景:實(shí)際開(kāi)發(fā)中,有的表數(shù)據(jù)量非常龐大,可能會(huì)采用分表方式進(jìn)行存儲(chǔ),比如每天生成一張表,表的名字與日期掛鉤,例如:2022年8月1日生成的表:t_user20220108。2000年1月1日生成的表:t_user20000101。此時(shí)前端在進(jìn)行查詢的時(shí)候會(huì)提交一個(gè)具體的日期,比如前端提交的日期為:2000年1月1日,那么后端就會(huì)根據(jù)這個(gè)日期動(dòng)態(tài)拼接表名為:t_user20000101。有了這個(gè)表名之后,將表名拼接到sql語(yǔ)句當(dāng)中,返回查詢結(jié)果。那么大家思考一下,拼接表名到sql語(yǔ)句當(dāng)中應(yīng)該使用#{} 還是 ${} 呢?
使用#{}會(huì)是這樣:select * from 't_car'
使用${}會(huì)是這樣:select * from t_car
向SQL語(yǔ)句當(dāng)中拼接表名,就需要使用${}
現(xiàn)實(shí)業(yè)務(wù)當(dāng)中,可能存在分表存儲(chǔ)的數(shù)據(jù)的情況,因?yàn)橐粡埍泶娴脑挘瑪?shù)據(jù)量太大了,查詢效率比較低。
可以將這些數(shù)據(jù)有規(guī)律的分表存儲(chǔ),這樣在查詢的時(shí)候效率就比較高,因?yàn)閽呙璧臄?shù)據(jù)量變小了
日志表,專門存儲(chǔ)日志信息,如果t_long 只有一張表,這張表中每一天都會(huì)產(chǎn)生很多的log,慢慢的,這個(gè)表中數(shù)據(jù)會(huì)很多,
怎么解決呢
可以每天生成一個(gè)新表,每張表以當(dāng)天日期作為名稱,例如:
t_log_202209801
t_log_20220902
你想知道某一天的日志信息怎么辦呢?
假設(shè)今天20220901,那么直接查:t_log-20220901的表即可。
下面是我們的t_log_20220902 的數(shù)據(jù)表信息
對(duì)應(yīng)數(shù)據(jù)表的 POJO 類設(shè)計(jì):
package com.rainbowsea.mybatis.pojo; public class Log { private Integer id; private String log; private String time; public Log() { } public Log(Integer id, String log, String time) { this.id = id; this.log = log; this.time = time; } @Override public String toString() { return "Log{" + "id=" + id + ", log='" + log + '\'' + ", time='" + time + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLog() { return log; } public void setLog(String log) { this.log = log; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
對(duì)應(yīng)的LogMapper 的接口信息編寫。
首先我們使用 ${} 的方式測(cè)試運(yùn)行。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Log; import java.util.List; public interface LogMapper { /** * 根據(jù)日期查詢不同的表,獲取表中所有的日志 * * @param date * @return */ List<Log> selectAllByTable(String date); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.LogMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log">--> <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log"> select * from t_log_${date}; </select> </mapper>
package com.rainbowsea.test; import com.rainbowsea.mybatis.mapper.LogMapper; import com.rainbowsea.mybatis.pojo.Log; import com.rainbowsea.mybatis.utils.SqlSessionUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class LogMapperTest { @Test public void testSelectAllByTable() { SqlSession sqlSession = SqlSessionUtil.openSession(); LogMapper mapper = sqlSession.getMapper(LogMapper.class); List<Log> logs = mapper.selectAllByTable("20220902"); logs.forEach(log -> { System.out.println(log); }); } }
使用 ${} 拼接上的結(jié)果為:select * from t_log_20220902; 直接就是 20220902 的值,沒(méi)有加單引號(hào)/雙引號(hào)。
使用 #{ } 的方式是會(huì)帶上 ‘’ 單引號(hào)的。
使用 #{} 拼接上的結(jié)果為:select * from t_log_‘20220902’; 加上的單引號(hào)后拼接上去,t_log_20220902是一個(gè)數(shù)據(jù)表名的,加了單引號(hào)后,MySQL 就找不到該數(shù)據(jù)表了,編譯無(wú)法通過(guò)了。
3.1.2 批量刪除
業(yè)務(wù)背景:一次刪除多條記錄。
批量刪除:一次刪除多條記錄
批量刪除的SQL語(yǔ)句有兩種寫法:
第一種:or: delete form t_car where id =1 or id = 2 or id = 3
第二種:delete from t_car where id in(1,2,3)
假設(shè)現(xiàn)在使用in的方式處理,前端傳過(guò)來(lái)的字符串:1, 2, 3
如果使用mybatis處理,應(yīng)該使用#{} 還是 ${}
使用#{} :delete from t_user where id in('1,2,3') 會(huì)加上單引號(hào), 執(zhí)行錯(cuò)誤:1292 - Truncated incorrect DOUBLE value: '1,2,3'
使用${} :delete from t_user where id in(1, 2, 3),執(zhí)行成功
所以我們要采用 ${} ,不加單引號(hào)的形式。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; /** * 封裝汽車相關(guān)信息的pojo類,普通的Java類 */ public interface CarMapper { /** * 批量刪除,根據(jù)id * @param ids * @return */ int deleteBath(String ids); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <delete id="deleteBath"> delete from t_car where id in(${ids}) </delete> </mapper>
Java程序測(cè)試,
注意因?yàn)槲覀冞@里是對(duì)數(shù)據(jù)表進(jìn)行的修改操作的,所以需要提交一下數(shù)據(jù)給數(shù)據(jù)庫(kù)。
這里我們刪除 id為(121,122,123) 三條記錄。
package com.rainbowsea.test; import com.rainbowsea.mybatis.mapper.CarMapper; import com.rainbowsea.mybatis.pojo.Car; import com.rainbowsea.mybatis.utils.SqlSessionUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class TestCarMapper { @Test public void testDeleteBath() { SqlSession sqlSession = SqlSessionUtil.openSession(); CarMapper mapper = sqlSession.getMapper(CarMapper.class); int count = mapper.deleteBath("121,122,123"); sqlSession.commit(); sqlSession.close(); } }
如果使用 #{} 是將 使用#{} :delete from t_user where id in('121,122,123') 會(huì)加上單引號(hào), 執(zhí)行錯(cuò)誤:1292 - Truncated incorrect DOUBLE value: '121,122,123'
3.1.3 模糊查詢
需求:查詢小米系列的汽車?!局灰放芺rand中含有小米兩個(gè)字的都查詢出來(lái)?!?/p>
關(guān)于模糊查詢有四種方案:
方案一:'%${brand}%'方案二: concat('%','${brand}','%')方案三: count函數(shù),這個(gè)是mysql數(shù)據(jù)庫(kù)當(dāng)中的一個(gè)函數(shù),專門進(jìn)行字符串拼接的 concat('%',#{brand},'%')方案四: "%"#{brand}"%", 這種方式比較常用,也避免了SQL注入的問(wèn)題。
方案一:'%${brand}%'
方案二: concat('%','${brand}','%')
方案三: count函數(shù),這個(gè)是mysql數(shù)據(jù)庫(kù)當(dāng)中的一個(gè)函數(shù),專門進(jìn)行字符串拼接的 concat('%',#{brand},'%')
方案四: "%"#{brand}"%"
四種方式:總的來(lái)說(shuō),都是為了拼接成字符串,把不是字符串的想辦法拼接成字符串 。
3.1.3.1 使用 ${ }的方式
方案一:'%${brand}%'
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; /** * 封裝汽車相關(guān)信息的pojo類,普通的Java類 */ public interface CarMapper { /** * 根據(jù)汽車品牌進(jìn)行模糊查詢 * @param brand * @return */ List<Car> selectByBrandLike(String brand); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where brand like '%${brand}%' </select> </mapper>
Java程序運(yùn)行結(jié)果:
方案二: concat('%','${brand}','%')
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where brand like concat('%','${brand}','%') </select> </mapper>
3.1.3.2 使用 #{ } 的方式
方案三: count函數(shù),這個(gè)是mysql數(shù)據(jù)庫(kù)當(dāng)中的一個(gè)函數(shù),專門進(jìn)行字符串拼接的 concat('%',#{brand},'%')
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where brand like concat('%',#{brand},'%') </select> </mapper>
方案四: "%"#{brand}"%", 這種方式比較常用,也避免了SQL注入的問(wèn)題。
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> <!-- 這樣因?yàn)椋簲?shù)據(jù)表的字段名和我們定義POJO類的屬性名不一致,要將二者保持一致,用 關(guān)鍵字 as 定義別名的方式保持一致--> <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where brand like "%"#{brand}"%" </select> </mapper>
測(cè)試運(yùn)行:
4. typeAliases 別名定義的使用
我們來(lái)觀察一下CarMapper.xml中的配置信息:
resultType 屬性用來(lái)指定查詢結(jié)果集的封裝類型,這個(gè)名字太長(zhǎng),可以起別名嗎?可以。
在 mybatis-config.xml 文件中使用 typeAliases 標(biāo)簽來(lái)起別名,包括兩種方式:
4.1 typeAliases 的第一種方式:typeAlias
<!-- 起別名--> <typeAliases> <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias> <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias> </typeAliases>
<?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> <!-- 起別名--> <typeAliases> <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias> <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias> </typeAliases> <environments default="mybatis"> <environment id="mybatis"> <!-- MANAGED 沒(méi)有用第三框架管理的話,都是會(huì)被提交的,沒(méi)有事務(wù)上的管理了 --> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="MySQL123"/> </dataSource> </environment> </environments> <mappers> <!-- 這里也是可以使用 package 包名掃描,但是同樣的:對(duì)應(yīng)接口路徑要一致,接口名一致--> <mapper resource="CarMapper.xml"></mapper> <mapper resource="LogMapper.xml"></mapper> </mappers> </configuration>
- 首先要注意typeAliases標(biāo)簽的放置位置,如果不清楚的話,可以看看錯(cuò)誤提示信息。
- typeAliases標(biāo)簽中的typeAlias可以寫多個(gè)。
- typeAlias:
- type屬性:指定給哪個(gè)類起別名
- alias屬性:別名。
- alias屬性不是必須的,如果缺省的話,type屬性指定的類型名的簡(jiǎn)類名作為別名。
- alias是大小寫不敏感的。也就是說(shuō)假設(shè)alias="Car",再用的時(shí)候,可以CAR,也可以car,也可以Car,都行、
注意:<namespace = “”>接口,一定要為全限定類名(帶有包名),不可以用別名機(jī)制
測(cè)試:
特別的: 省略 alias 屬性之后,別名就是類的簡(jiǎn)名了,比如 :com.rainbowsea.mybatis.pojo.Car 的別名就是 Car/CAR/cAR,不區(qū)分大小寫的。
<!-- 起別名--> <!-- 省略alias 屬性之后,別名就是類的簡(jiǎn)名了,比如:com.rainbowsea.mybatis.pojo.Car 的別名就是 Car/CAR/cAR 不區(qū)分大小寫的。--> <typeAliases> <typeAlias type="com.rainbowsea.mybatis.pojo.Car" ></typeAlias> <typeAlias type="com.rainbowsea.mybatis.pojo.Log"></typeAlias> </typeAliases>
4.2 typeAliases 的第二種方式:package
如果一個(gè)包下的類太多,每個(gè)類都要起別名,會(huì)導(dǎo)致 typeAlias 標(biāo)簽配置較多,所以mybatis用提供package的配置方式,只需要指定包名,該包下的所有類都自動(dòng)起別名,別名就是簡(jiǎn)類名。并且別名不區(qū)分大小寫。
注意:<namespace = “”>接口,一定要為全限定類名(帶有包名),不可以用別名機(jī)制
這種方式是最常用的。
<!-- 別名 --> <typeAliases> <!--使用 <package> 還可以將這個(gè)包下的所有的類的全部自動(dòng)起別名,別名就是簡(jiǎn)名,不區(qū)分大小寫--> <package name="com.rainbowsea.mybatis.pojo"/> </typeAliases>
測(cè)試:
注意:
使用 <package>還可以將這個(gè)包下的所有的類的全部自動(dòng)起別名,別名就是簡(jiǎn)名,不區(qū)分大小寫
,所有的別名不區(qū)分大小寫。但是:namespace 不能使用別名機(jī)制
同時(shí)需要按照一定的順序放置,放到指定的順序當(dāng)中去
5. mappers 路徑設(shè)置的使用
SQL映射文件的配置方式包括四種:
- resource:從類路徑中加載
- url:從指定的全限定資源路徑中加載
- class:使用映射器接口實(shí)現(xiàn)類的完全限定類名
- package:將包內(nèi)的映射器接口實(shí)現(xiàn)全部注冊(cè)為映射器
5.1 mapper 標(biāo)簽下的 resource 屬性的使用
這種方式是從類路徑中加載配置文件,所以這種方式要求SQL映射文件必須放在resources目錄下或其子目錄下。
<mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
5.2 mapper 標(biāo)簽下的 url 屬性的使用
這種方式是一種絕對(duì)路徑的方式,這種方式不要求配置文件必須放到類的路徑當(dāng)中,哪里都行,只要提供了一個(gè)絕對(duì)路徑就行,這種方式使用極少,因?yàn)橐浦残蕴盍耍ú⒉皇撬缘南到y(tǒng)都有盤符的說(shuō)法的)。
**需要注意的是:要三個(gè)\\\ ,才表示兩個(gè) \\ **
<mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
5.3 mapper 標(biāo)簽下的 class 屬性的使用
Class: 這位置提供的是 mapper 接口的全限定接口名,必須帶有包名(就是要一定要和對(duì)應(yīng)接口的路徑是一致的,一致的,一致的)
如果使用這種方式必須滿足以下條件:
- SQL映射文件和mapper接口放在同一個(gè)目錄下。
- SQL映射文件的名字也必須和mapper接口名一致。
<mapper class="com.rainbowsea.mybatis.mapper.CarMapper"><mapper> 如果你class指定是:com.rainbowsea.mybatis.mapper.CarMapper 那么mybatis框架會(huì)自動(dòng)去com/rainbowsea/mybatis/mapper/CarMapper/的目錄下找,注意是 / 注意:也就是說(shuō),如果你采用這種方式,那么你必須保證:CarMapper.xml文件和CarMapper接口必須在同一個(gè)目錄下,并且名字也是一致的 CarMapper接口——》CarMapper.xml LogMapper接口——> LogMapper.xml
提醒:
在IDEA的resources 目錄下新建多重目錄的話,必須是這樣創(chuàng)建:
com/rianbowsea/mybatis/mapper/
不然是
com.rianbowsea.mybatis.mapper 這是建包了
保持一致的同時(shí),名稱也要是一致的
<mappers> <!-- 這里也是可以使用 package 包名掃描,但是同樣的:對(duì)應(yīng)接口路徑要一致,接口名一致--> <mapper class="com.rainbowsea.mybatis.mapper.CarMapper"></mapper> <mapper class="com.rainbowsea.mybatis.mapper.LogMapper"></mapper> </mappers>
測(cè)試運(yùn)行:
運(yùn)行程序:正常?。?!
5.4 package 標(biāo)簽的使用
如果class較多,可以使用這種package的方式,但前提條件和上一種(mapper 標(biāo)簽下的class 屬性的使用)方式一樣。
如果使用這種方式必須滿足以下條件:
- SQL映射文件和mapper接口放在同一個(gè)目錄下。
- SQL映射文件的名字也必須和mapper接口名一致。
這種方式是最常用的。
<!-- 這里也是可以使用 package 包名掃描,但是同樣的:對(duì)應(yīng)接口路徑要一致,接口名一致--> <package name="com.rainbowsea.mybatis.mapper"></package>
運(yùn)行測(cè)試:
6. 在 IDEA 中自定義配置文件模板
mybatis-config.xml 和 SqlMapper.xml ,logback 文件可以在IDEA中提前創(chuàng)建好模板,以后通過(guò)模板創(chuàng)建配置文件。
mybatis 核心配置文件的模板內(nèi)容 :
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 控制臺(tái)輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!--mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志輸出級(jí)別,logback日志級(jí)別包括五個(gè):TRACE < DEBUG < INFO < WARN < ERROR --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
logback 的日志配置模板內(nèi)容 :
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 控制臺(tái)輸出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> </appender> <!--mybatis log configure--> <logger name="com.apache.ibatis" level="TRACE"/> <logger name="java.sql.Connection" level="DEBUG"/> <logger name="java.sql.Statement" level="DEBUG"/> <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志輸出級(jí)別,logback日志級(jí)別包括五個(gè):TRACE < DEBUG < INFO < WARN < ERROR --> <root level="DEBUG"> <appender-ref ref="STDOUT"/> <appender-ref ref="FILE"/> </root> </configuration>
SqLMapper的配置模板內(nèi)容:
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace=""> <!-- id 要是 namespace 對(duì)應(yīng)接口上的方法名: --> </mapper>
**前提是:主鍵是自動(dòng)生成的。 **
業(yè)務(wù)背景:一個(gè)用戶有多個(gè)角色。
插入一條新的記錄之后,自動(dòng)生成了主鍵,而這個(gè)主鍵需要在其他表中使用時(shí)。
插入一個(gè)用戶數(shù)據(jù)的同時(shí)需要給該用戶分配角色:需要將生成的用戶的id插入到角色表的user_id字段上。
第一種方式:可以先插入用戶數(shù)據(jù),再寫一條查詢語(yǔ)句獲取id,然后再插入user_id字段?!颈容^麻煩】
第二種方式:mybatis提供了一種方式更加便捷。
<!-- userGeneratedKeys = "true" 使用自動(dòng)生成的主鍵值 keyProperty="id" 指定主鍵值賦值給對(duì)象的哪個(gè)屬性,這個(gè)就表示將主鍵值給Car對(duì)象的 id屬性。 注意:這個(gè) keyProperty 指定的值,一定要和對(duì)應(yīng)上的 pojo 對(duì)象類上的屬性一致,不然,不行的 --> <insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" > insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
userGeneratedKeys = "true" 使用自動(dòng)生成的主鍵值,false 則是不使用自動(dòng)生成的主鍵值了
keyProperty="id" 指定主鍵值賦值給POJO類(ORM映射)對(duì)象的哪個(gè)屬性,這個(gè)就表示將主鍵值給Car對(duì)象的 id屬性。
注意:這個(gè) keyProperty 指定的值,一定要和對(duì)應(yīng)上的 pojo 對(duì)象類上的屬性一致,不然,不行的。
package com.rainbowsea.mybatis.mapper; import com.rainbowsea.mybatis.pojo.Car; import java.util.List; /** * 封裝汽車相關(guān)信息的pojo類,普通的Java類 */ public interface CarMapper { /** * 插入 Car 信息,并且使用生成的主鍵值 * @param car * @return */ int insertCarUserGeneratedKey(Car car); }
<?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"> <!--namespace 一定要是:對(duì)應(yīng)的接口的全限定類名--> <mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper"> <!-- userGeneratedKeys = "true" 使用自動(dòng)生成的主鍵值 keyProperty="id" 指定主鍵值賦值給對(duì)象的哪個(gè)屬性,這個(gè)就表示將主鍵值給Car對(duì)象的 id屬性。 注意:這個(gè) keyProperty 指定的值,一定要和對(duì)應(yīng)上的 pojo 對(duì)象類上的屬性一致,不然,不行的 --> <insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id"> insert into t_car values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType}) </insert> </mapper>
運(yùn)行測(cè)試:
8. 總結(jié):
1.{ } 與 ${ } 的區(qū)別和使用
- #{ } :先編譯 SQL 語(yǔ)句,再對(duì)占位符傳值,底層是 PrepareedStatement 實(shí)現(xiàn),可以防止 SQL 注入,比較常用- ${ }:先進(jìn)行SQL語(yǔ)句的拼接,然后再對(duì)SQL語(yǔ)句進(jìn)行編譯,底層是 Statement 實(shí)現(xiàn),這種方式存在 SQL注入現(xiàn)象,SQL注入的風(fēng)險(xiǎn),簡(jiǎn)單的說(shuō)就是,直接將傳入的值拼接為了SQL語(yǔ)句,然后再執(zhí)行的)。只有在需要進(jìn)行SQL語(yǔ)句關(guān)鍵字拼接的情況下才會(huì)用到。- 簡(jiǎn)單的說(shuō)一個(gè)區(qū)別就是:#{} 傳的值是帶有 '' 單引號(hào)的,而 ${} 傳的值是(直接就是值,沒(méi)有單引號(hào),或者是雙引號(hào),兩個(gè)都沒(méi)有)
2.什么情況下必須使用 $
拼接表名批量刪除模糊查詢
3.typeAliases 別名定義的使用,注意:<namespace = “”>接口,一定要為全限定類名(帶有包名),不可以用別名機(jī)制 ,建議采用第二種 package 方式,,同時(shí)注意:typeAliases 的正確順序和位置,可以參考報(bào)錯(cuò)信息,進(jìn)行修正。
<!-- 別名 --> <typeAliases> <!--使用 <package> 還可以將這個(gè)包下的所有的類的全部自動(dòng)起別名,別名就是簡(jiǎn)名,不區(qū)分大小寫--> <package name="com.rainbowsea.mybatis.pojo"/> </typeAliases>
???????4.mappers 路徑設(shè)置的使用 ,建議采用 package 標(biāo)簽的方式,注意條件,兩個(gè)條件的一致性。
- SQL映射文件和mapper接口放在同一個(gè)目錄下。
- SQL映射文件的名字也必須和mapper接口名一致。
5.???????在 IDEA 中自定義配置文件模板
6.插入數(shù)據(jù)時(shí)獲取自動(dòng)生成的主鍵的值,**前提是:在對(duì)應(yīng)數(shù)據(jù)表中的主鍵是自動(dòng)生成的(自增的方式才行)。 **
<!-- userGeneratedKeys = "true" 使用自動(dòng)生成的主鍵值 keyProperty="id" 指定主鍵值賦值給對(duì)象的哪個(gè)屬性,這個(gè)就表示將主鍵值給Car對(duì)象的 id屬性。 注意:這個(gè) keyProperty 指定的值,一定要和對(duì)應(yīng)上的 pojo 對(duì)象類上的屬性一致,不然,不行的 --> <insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" > insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
9. 最后:
到此這篇關(guān)于MyBatis 的在使用上的注意事項(xiàng)及其辨析的文章就介紹到這了,更多相關(guān)MyBatis使用注意事項(xiàng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 的雙重分發(fā)與 Visitor 模式實(shí)例詳解
這篇文章主要介紹了Java 的雙重分發(fā)與 Visitor 模式實(shí)例詳解,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07SparkStreaming整合Kafka過(guò)程詳解
這篇文章主要介紹了SparkStreaming整合Kafka過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01詳解Java中的println輸入和toString方法的重寫問(wèn)題
這篇文章主要介紹了Java中的println輸入和toString方法的重寫,一個(gè)對(duì)象數(shù)組在調(diào)用Arrays.toString打印時(shí),相當(dāng)于遍歷數(shù)組,然后打印里邊每個(gè)對(duì)象,這再打印對(duì)象就調(diào)用對(duì)象自己的toString了,需要的朋友可以參考下2022-04-04Spring MVC請(qǐng)求參數(shù)的傳遞方式
Spring MVC是一種基于Model-View-Controller(MVC)設(shè)計(jì)模式的輕量級(jí)Web框架,用于Java應(yīng)用程序的開(kāi)發(fā),在處理HTTP請(qǐng)求時(shí),Spring MVC會(huì)涉及到請(qǐng)求參數(shù)的傳遞,本文給大家介紹了Spring MVC請(qǐng)求參數(shù)的傳遞方式,需要的朋友可以參考下2024-10-10