javaEE-mybatis操作數據庫

前言?

在MySQL的學習階段,我們知道了如何使用JDBC去操作,也正是因為學習了JDBC也知道其操作的繁瑣,每次的CRUD操作都需要從數據庫連接池中去獲取數據庫連接,然后再編寫SQL語句,并綁定對應的參數,接著通過連接執行SQL語句,如果返回值的話還得處理返回值,最后還得釋放連接等資源。明明在增刪改查階段只需要修改SQL語句以及對應的參數和處理返回結果即可,但還是存在很多重復性的操作的。因此,Mybatis就由此而誕生了,Mybatis實現了對JDBC的進一步簡化與封裝,讓Java開發者可以更加簡單的去操作數據庫。

Mybatis快速入門

Mybatis操作數據庫的步驟

1、準備工作(創建SpringBoot項目、數據庫表的準備、Java實體類的準備)

2、引入Mybatis的相關依賴,配置Mybatis(數據庫連接信息)

3、編寫SQL語句(使用注解或者xml)

4、測試代碼

File -> New -> Project:

接下來創建對應的數據表以及填充數據:

-- 創建表[用戶表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默認',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-刪除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; -- 添加用戶信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

創建Java中對應的實體類:

@Data
public class UserInfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;
}

配置數據庫連接:

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

接下來就是編寫查詢SQL了(先使用看看效果,后面再學習)

@Mapper // 只能使用@Mapper注解
public interface UserInfoMapper { // 在mapper包下(屬于數據層)@Select("select * from userinfo")List<UserInfo> selectAll();
}

接著在UserInfoMapper這個類所在的文件中,右鍵找到Generate,點擊 Test?

按照上述步驟,就會生成一個測試類:

@SpringBootTest // 加載spring的運行環境(為了DI)
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Test// 標記該方法為測試方法,可以直接運行void selectAll() {List<UserInfo> userInfos = userInfoMapper.selectAll();System.out.println(userInfos);}
}

?我們只需要在@Test注解的左側點擊run即可運行該方法(運行結果如下)

從上述步驟來看,從編寫SQL語句到測試的步驟非常簡單。 所以的一切都是spring幫我們做好了,我們只需要使用即可。

注意:

1、只有查詢的SQL中對應的字段才會在對象對應的屬性中有值。但細心的小伙伴可能會發現,刪除標志、創建時間、更新時間 也同樣沒有值,這個我們后面再學習。

2、Mybatis也是一個框架,是獨立于Spring框架的,Mybatis是對JDBC的封裝,即JDBC在Maven項目中可以使用,同樣Mybatis也能在Maven項目中使用。但要注意的是上面的依賴是只能在SpringBoot項目中使用,在Maven項目使用的依賴并不是長那樣。

既然已經知道了Mybatis框架的優點,接下來就具體學習Mybatis是如何操作數據庫的。

Mybatis的基礎操作(注解實現接口類)

使用Mybatis操作數據庫的話,我們都是創建一個mapper包用來存放具體的操作接口,然后在對應的接口中去編寫方法。接著就只需要Mybatis來實現該接口方法,然后我們再將該接口類注冊到Spring的容器中,使用容器來實現依賴注入,接著就是調用對應的操作方法即可。 Mybatis 實現 該接口方法的方式有兩種:1、使用注解;2、使用xml。我們先來學習簡單的注解。

打印日志

在Mybatis中,我們可以使用日志,來查看SQL語句的執行、參數的傳遞,以及執行的結果。因此我們需要先配置:

# 配置Mybatis日志
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

查詢

如果我們想要查詢數據庫中的數據,只需要創建一個接口方法,并在上方加上@Select注解,里面加上查詢的語句即可。

需求:查詢id=1的用戶

@Mapper // 只能使用@Mapper注解
public interface UserInfoMapper { // 在mapper包下(屬于數據層)@Select("select * from userinfo")List<UserInfo> selectAll();@Select("select * from userInfo where id=1")UserInfo selectById();}

測試類?

@SpringBootTest // 加載spring的運行環境(為了DI)
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Test// 標記該方法為測試方法,可以直接運行void selectAll() {List<UserInfo> userInfos = userInfoMapper.selectAll();System.out.println(userInfos);}@Testvoid selectById() {UserInfo Uer1=userInfoMapper.selectById();}
}

結果

雖然上述方式能夠查詢id = 1的數據,但是這種方法是寫死的,即通過手動指定來查詢的數據,但在實際開發中,往往是需要動態的數值。這里就需要用到 "#{參數名}" 的方式了。

  @Select("select * from userInfo where id=#{id}")UserInfo selectById2(Integer id);

測試類?

  void selectById2() {UserInfo Uer2=userInfoMapper.selectById2(2);System.out.println(Uer2);}

結果

注意:

1、如果接口方法里面的形參只有一個時,#{參數名},這里的參數名可以是任意值。

2、如果想改名的話,需要通過@Param注解將修改后的參數名寫入其中。

我們在上?查詢時發現, 有?個字段是沒有賦值的, 只有Java對象屬性和數據庫字段?模?樣時, 才會進行賦值

解決方法:

1. 起別名
2. 結果映射
3. 開啟駝峰命名
起別名
在SQL語句中,給列名起別名,保持別名和實體類屬性名?樣
@Select("select id, username, `password`, age, gender, phone, delete_flag as 
deleteFlag, " +"create_time as createTime, update_time as updateTime from user_info")
public List<UserInfo> queryAllUser();
結果映射
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from user_info")
@Results({@Result(column = "delete_flag",property = "deleteFlag"),@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();
如果其他SQL, 也希望可以復?這個映射關系, 可以給這個Results定義?個名稱
使? id 屬性給該 Results 定義別名, 使? @ResultMap 注解來復?其他定義的 ResultMap
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from user_info")
@Results(id = "resultMap",value = {@Result(column = "delete_flag",property = "deleteFlag"),@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();
@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time " +"from user_info where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);

?開啟駝峰命名(推薦)

通常數據庫列使?蛇形命名法進?命名(下劃線分割各個單詞), ? Java 屬性?般遵循駝峰命名法約定.
為了在這兩種命名?式之間啟??動映射,需要將 mapUnderscoreToCamelCase 設置為 true
mybatis:configuration:map-underscore-to-camel-case: true #配置駝峰?動轉換
駝峰命名規則: abc_xyz => abcXyz
? 表中字段名:abc_xyz
? 類中屬性名:abcXyz

?增加

增加數據,是通過@Insert注解實現的,只需要在里面加上對應的SQL語句即可。?

上述這種方式是比較繁瑣的,我們可以使用傳輸對象的方式:

Insert語句默認返回的是受影響的行數,但在有些情景下,數據插入之后,需要獲取的新插入的數據的id,提供給后續的操作。例如,訂單系統的中的下完訂單之后,后續的物流系統、庫存系統等都需要拿到訂單的id來進行操作。如果想要拿到自增的id,需要在接口方法上添加Options注解。

獲取自動生成的主鍵值:通過Options注解來獲取,其屬性useGeneratedKeys=true表示使用數據庫自增主鍵,keyColumn用于指定數據庫table中的主鍵,keyProperty用于指定傳入對象的屬性(綁定到對象的哪個屬性)。如果我們已經在數據庫表中指定了主鍵,并且數據庫中主鍵的名稱和Java屬性的名稱是相同的,那么keyColumn屬性可以省略。

   @Options(useGeneratedKeys = true, keyProperty = "id")@Insert("insert into userinfo (username, password, age, gender) values (#{username},#{password},#{age},#{gender})")Integer insert(UserInfo userInfo);

測試代碼

 @Testvoid insert() {UserInfo userInfo = new UserInfo();userInfo.setUsername("zhaoliu");userInfo.setPassword("zhaoliu");userInfo.setAge(18);userInfo.setGender(1);Integer result = userInfoMapper.insert(userInfo);System.out.println("result: "+result + ",id:"+userInfo.getId());}

?刪除

  @Delete("delete from userinfo where id = #{id}")Integer delete(Integer id);

更新

更新數據,是通過@Update注解實現的,只需要在里面加上對應的SQL語句即可。??

 @Update("update userInfo set username = #{username} where id = #{id}")Integer update(UserInfo userInfo);
 void update() {UserInfo userInfo=new UserInfo();userInfo.setUsername("lisi");userInfoMapper.selectById2(2);Integer result=userInfoMapper.update(userInfo);System.out.println(result);}

XML實現接口類?

快速上手?

配置數據源和導入依賴與前面是一樣的。下面是不一樣的配置:

1、配置 Mybatis 的 xml文件路徑

mybatis: # classpath 表示resources目錄下,整體表示的是 resources/mapper/所有xml文件# * 代表通配符,表示只要是 .xml 文件即可mapper-locations: classpath:mapper/*.xml

第二步同樣還是創建接口類,第三步不再是通過注解來實現SQL語句了,而是通過在配置的XML文件目錄下創建XML文件,在文件中編寫對應的實現SQL語句。

在新創建的XML文件中,添加下面的標簽:?

<?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 表示的該xml文件實現的接口類的全限定名稱 -->
<mapper namespace=""></mapper>

?這里的namespace的值就是UserInfoMapperXML文件的全限定類名。

例如:com.example.demo.UserInfoMapperXML

這里再介紹一個插件: mybatisx,

安裝該插件之后,會有兩個好處:

1、知道XML文件中的mapper標簽的namespace值是否綁定正確。如果綁定正確的話,對應的文件位置就會出現小鳥圖標,點擊該圖標可以在綁定的文件之中任意跳轉。?

?2、插件會自動校驗對應的接口類的接口方法是否生成了對應的實現標簽。如果生成對應的實現標簽的話,就會在對應的標簽和方法之間生成小鳥圖標,同樣可以實現自動跳轉,反之,如果沒有生成的話,就會在對應的接口類所在的方法上爆紅。

接下來,就可以在標簽中編寫對應的SQL語句了。

  <select id="select" resultType="com.example.demo.UserInfo">select * from userinfo;</select>

?最后就是測試對應的SQL語句了,同樣在接口類中,鼠標右鍵 -> Generate -> Test:

查詢?

根據id查詢:

System.out.println(userInfoMapperXML.select());
<select id="selectbyid" resultType="com.example.demo.UserInfo">select * from userinfo where id = #{id};</select>
 @Testvoid selectbyid() {System.out.println(userInfoMapperXML.selectbyid(3));}

根據 username 和 password 查詢:

// 根據username和password查詢
UserInfo selectByUsernameAndPassword(String username, String password);@Test
void selectByUsernameAndPassword() {System.out.println(userInfoMapperXML.selectByUsernameAndPassword("admin", "admin_123456"));
}

根據 對象 來查詢:

// 根據對象來查詢
UserInfo selectByUserInfo(UserInfo userInfo);<select id="selectByUserInfo" resultType="com.springboot.mybatisdemo2.pojo.UserInfo"><!-- 注意,這里#{}內部的是實體類的屬性名,其余的都是數據庫的字段 -->select * from user_info where username = #{username} and password = #{password}
</select>@Test
void selectByUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setUsername("admin");userInfo.setPassword("admin_123456");System.out.println(userInfoMapperXML.selectByUserInfo(userInfo));
}

由于之前在配置文件中,設置過了駝峰自動轉換,因此這里并不會出現對象屬性為null的情況。現在我們來看在XML文件中,使用起別名和result標簽如何解決。

<!-- 起別名 -->
<select id="selectAll2" resultType="com.springboot.mybatisdemo2.pojo.UserInfo">select id, username, password, age, gender, phone,delete_flag as deleteFlag,create_time as createTime,update_time as updateTime  from user_info
</select><!-- id是給別的標簽使用所引用的  type表示映射到哪個實體類上 -->
<resultMap id="resultMap" type="com.springboot.mybatisdemo2.pojo.UserInfo"><!-- 規范的寫法是要將數據表中所有字段和實體類的屬性相對應的,即使有些字段和屬性是對應上的,也最好要加上,因為是規范id 表示主鍵,result表示普通字段--><id column="id" property="id"/><result column="username" property="username"/><result column="password" property="password"/><result column="age" property="age"/><result column="gender" property="gender"/><result column="phone" property="phone"/><result column="delete_flag" property="deleteFlag"/><result column="create_time" property="createTime"/><result column="update_time" property="updateTime"/>
</resultMap><!-- resultMap表示要引用的映射關系 -->
<select id="selectAll3" resultType="com.springboot.mybatisdemo2.pojo.UserInfo" resultMap="resultMap">select * from user_info
</select>

增加

新增數據,傳遞參數:

// 新增數據:傳遞參數
Integer insertByUsernameAndPassword(String username, String password);<insert id="insertByUsernameAndPassword">insert into user_info (username, password) values (#{username}, #{password})
</insert>@Test
void insertByUsernameAndPassword() {System.out.println(userInfoMapperXML.insertByUsernameAndPassword("444", "444"));
}

新增數據,傳遞對象:

// 新增數據:傳遞對象
Integer insertByUserInfo(UserInfo userInfo);<insert id="insertByUserInfo">insert into user_info (username, password) values (#{username}, #{password})
</insert>@Test
void insertByUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setUsername("444");userInfo.setPassword("444");System.out.println(userInfoMapperXML.insertByUserInfo(userInfo));
}

新增數據,獲取自增id:

// 新增數據:獲取自增的id
Integer insertGetGeneratedKey(UserInfo userInfo);<insert id="insertGetGeneratedKey" useGeneratedKeys="true" keyProperty="id">insert into user_info (username, password) values (#{username}, #{password})
</insert>@Test
void insertGetGeneratedKey() {UserInfo userInfo = new UserInfo();userInfo.setUsername("444");userInfo.setPassword("444");System.out.println("受影響的行數: " + userInfoMapperXML.insertGetGeneratedKey(userInfo));System.out.println("獲取自增的id: " + userInfo.getId());
}

刪除

刪除數據,傳遞id:

// 刪除數據:傳遞參數
Integer deleteById(Integer id);<delete id="deleteById">delete from user_info where id = #{id}
</delete>@Test
void deleteById() {System.out.println(userInfoMapperXML.deleteById(40));
}

刪除數據,傳遞對象:?

// 刪除數據:傳遞對象
Integer deleteByUserInfo(UserInfo userInfo);<delete id="deleteByUserInfo">delete from user_info where username = #{username} and password = #{password}
</delete>@Test
void deleteByUserInfo() {UserInfo userInfo = new UserInfo();userInfo.setUsername("admin");userInfo.setPassword("admin");System.out.println(userInfoMapperXML.deleteByUserInfo(userInfo));
}

更新

更新數據,傳遞參數?

// 更新數據:傳遞參數
Integer updateByUsernameAndPassword(Long id, String username, String password);<update id="updateByUsernameAndPassword">update user_info set username = #{username}, password = #{password} where id = #{id}
</update>@Test
void updateByUsernameAndPassword() {System.out.println(userInfoMapperXML.updateByUsernameAndPassword(33 L, "admin", "admin"));
}

多表查詢

由于實際開發中,多表查詢的使用頻率不是很高,因此這里不再演示了。 感興趣可以看看別的博主文章

#{} 和 ${} 的區別

MyBatis 參數賦值有兩種?式, 咱們前?使?了 #{} 進?賦值, 接下來我們看下?者的區別
1 @Select("select username, `password`, age, gender, phone from user_info where 
id= #{id} ")
2 UserInfo queryById(Integer id);

1 select username, `password`, age, gender, phone from user_info where id= ?
我們輸?的參數并沒有在后?拼接,id的值是使? ? 進?占位. 這種SQL 我們稱之為"預編譯SQL"
我們把 #{} 改成 ${} 再觀察打印的?志
@Select("select username, `password`, age, gender, phone from user_info where id= ${id} ") 2 UserInfo queryById(Integer id); 

可以看到, 這次的參數是直接拼接在SQL語句中了
接下來我們再看String類型的參數
@Select("select username, `password`, age, gender, phone from user_info where 
username= #{name} ")
UserInfo queryByName(String name);

我們把 #{} 改成 ${} 再觀察打印的?志
@Select("select username, `password`, age, gender, phone from user_info where
username= ${name} ") 
UserInfo queryByName(String name);

可以看到, 這次的參數依然是直接拼接在SQL語句中了, 但是字符串作為參數時, 需要添加引號 '' , 使
? ${} 不會拼接引號 '' , 導致程序報錯.
修改代碼如下:
@Select("select username, `password`, age, gender, phone from user_info where 
username= '${name}' ")
UserInfo queryByName(String name);
再次運?, 結果正常返回
從上?兩個例?可以看出:
#{} 使?的是預編譯SQL, 通過 ? 占位的?式, 提前對SQL進?編譯, 然后把參數填充到SQL語句
中. #{} 會根據參數類型, ?動拼接引號 '' .
${} 會直接進?字符替換, ?起對SQL進?編譯. 如果參數為字符串, 需要加上引號 '' .
參數為數字類型時, 也可以加上, 查詢結果不變, 但是可能會導致索引失效, 性能下降.
#{} 和 ${} 的區別就是預編譯SQL和即時SQL 的區別
簡單回顧:
當客?發送?條SQL語句給服務器后, ?致流程如下:
1. 解析語法和語義, 校驗SQL語句是否正確
2. 優化SQL語句, 制定執?計劃
3. 執?并返回結果
?條 SQL如果?上述流程處理, 我們稱之為 Immediate Statements(即時 SQL)
1. 性能更?
絕?多數情況下, 某?條 SQL 語句可能會被反復調?執?, 或者每次執?的時候只有個別的值不同
如 select 的 where ?句值不同, update 的 set ?句值不同, insert 的 values 值不同). 如果每次都需要 經過上?的語法解析, SQL優化、SQL編譯等,則效率就明顯不?了.
預編譯SQL,編譯?次之后會將編譯后的SQL語句緩存起來,后?再次執?這條語句時,不會再次編譯
(只是輸?的參數不同), 省去了解析優化等過程, 以此來提?效率
2. 更安全(防?SQL注?)
SQL注?:是通過操作輸?的數據來修改事先定義好的SQL語句,以達到執?代碼對服務器進?攻擊的 ?法。
由于沒有對??輸?進?充分檢查,?SQL?是拼接?成,在??輸?參數時,在參數中添加?些
SQL關鍵字,達到改變SQL運?結果的?的,也可以完成惡意攻擊。
@Select("select username, `password`, age, gender, phone from user_info where 
username= '${name}' ")
List<UserInfo> queryByName(String name);
sql 注?代碼: ' or 1='1
正常訪問情況:
@Test
void queryByName() {List<UserInfo> userInfos = userInfoMapper.queryByName("admin");System.out.println(userInfos);
} 
結果運?正常

SQL注?場景:

?

1 @Test
2 void queryByName() {
3 List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");
4 System.out.println(userInfos);
5 }
結果依然被正確查詢出來了, 其中參數 or被當做了SQL語句的?部分
可以看出來, 查詢的數據并不是??想要的數據. 所以?于查詢的字段,盡量使? #{} 預查詢的?式
SQL注?是?種?常常?的數據庫攻擊?段, SQL注?漏洞也是?絡世界中最普遍的漏洞之?.
如果發?在??登錄的場景中, 密碼輸?為 ' or 1='1 , 就可能完成登錄
排序功能
從上?的例?中, 可以得出結論: ${} 會有SQL注?的?險, 所以我們盡量使?#{}完成查詢
既然如此, 是不是 ${} 就沒有存在的必要性了呢?
當然不是
接下來我們看下${}的使?場景
Mapper實現
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from user_info order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);
使? ${sort} 可以實現排序查詢, ?使? #{sort} 就不能實現排序查詢了.
注意: 此處 sort 參數為String類型, 但是SQL語句中, 排序規則是不需要加引號 '' 的, 所以此時的 ${sort} 也不加引號
我們把 ${} 改成 #{}
可以發現, 當使? #{sort} 查詢時, asc 前后?動給加了引號, 導致 sql 錯誤
#{} 會根據參數類型判斷是否拼接引號 ''
如果參數類型為String, 就會加上 引號.
除此之外, 還有表名作為參數時, 也只能使? ${}
Like查詢
like 使? #{} 報錯
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from user_info where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);
把 #{} 改成 ${} 可以正確查出來, 但是${}存在SQL注?的問題, 所以不能直接使? ${}.
解決辦法: 使? mysql 的內置函數 concat() 來處理,實現代碼如下:
@Select("select id, username, age, gender, phone, delete_flag, create_time, 
update_time " +"from user_info where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

數據庫連接池?

我們前面在學習JDBC時,每次增刪改查操作都需要去手動創建連接,當SQL語句執行完畢之后,就需要去手動釋放連接,整個過程是比較消耗資源的,特別是頻繁創建與銷毀連接時。前面學習多線程時,也是需要手動創建新線程,后來學習了如何使用線程池之后,就可以直接向線程池中拿線程,使用完之后,繼續放到線程池中,這樣的做法就是避免了頻繁創建與銷毀帶來的時間開銷。同樣,數據庫連接也可以存放到數據庫連接池中,SpringBoot項目也是封裝了數據庫連接池:

這里使用到的數據庫連接池是 Hikari,如果我們想要使用別的數據庫連接池的話,也是可以在pom文件中導入依賴的。
<!-- springboot3.x版本 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency><!-- springboot2.x版本 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

再次啟動測試用例,觀察控制臺的輸出結果:

好啦!本期 初始 JavaEE篇 —— Mybatis操作數據庫(上)的學習之旅 就到此結束啦!我們下一期再一起學習吧!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86583.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86583.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86583.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

移動端測試——如何解決iOS端無法打開彈窗式網頁(Webkit)

目錄 一、什么是webkit&#xff1f; 1. 核心定義 2. iOS 的特殊限制 3. 彈窗攔截的邏輯 二、為什么 iOS 必須用 WebKit&#xff1f; 1. 蘋果的官方理由 2. 實際后果 3.然而…… 三、如何解決iOS端無法打開彈窗式網頁&#xff1f; 1.用戶 1.1 safari瀏覽器 1.2 夸克…

【github】從本地更新倉庫里的文件筆記

1. 打開GitHub官網&#xff0c;并登錄到您的賬戶。 2. 在頁面右上角的搜索欄中&#xff0c;輸入您要更新的倉庫名稱&#xff0c;并選擇相應的倉庫進入。 3. 在倉庫頁面中&#xff0c;找到并點擊紅色的“Code”按鈕&#xff0c;然后復制倉庫的HTTPS或者SSH鏈接。 4. 右鍵包含…

Excel基礎:數據編輯

Excel是Windows下最常用的數據處理工具&#xff0c;本文詳細介紹Excel的數據編輯功能&#xff0c;熟練掌握編輯技巧能可以極大提升工作效率&#xff0c;文章最后附加了一張總結思維導圖&#xff0c;方便大家查找和記憶。 文章目錄 一、數據輸入1.1 覆蓋輸入1.2 追加輸入1.3 任…

JavaScript中Object()的解析與應用

在JavaScript中&#xff0c;Object() 是一個基礎構造函數&#xff0c;用于創建對象或轉換值為對象類型。它既是語言的核心組成部分&#xff0c;也提供了一系列靜態方法用于對象操作。以下是詳細解析和應用示例&#xff1a; 一、Object() 的基本行為 作為構造函數&#xff08;…

stream使用案例

1.1 查找所有的偶數并求和 public static void p1() { List<Integer> numbers Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int sum numbers.stream() .filter(num -> num % 2 0) .mapToInt(Integer::intValue) .sum() ; System.err.printf…

力扣 刷題(第七十一天)

靈感來源 - 保持更新&#xff0c;努力學習 - python腳本學習 4的冪 解題思路 位運算條件&#xff1a;4 的冪的二進制表示中只有一個 1&#xff0c;且位于奇數位&#xff08;如 4 100&#xff0c;4 10000&#xff09;。模運算條件&#xff1a;4 的冪減 1 后能被 3 整除&…

深度學習使用Pytorch訓練模型步驟

訓練模型是機器學習和深度學習中的核心過程&#xff0c;旨在通過大量數據學習模型參數&#xff0c;以便模型能夠對新的、未見過的數據做出準確的預測。 訓練模型通常包括以下幾個步驟&#xff1a; 1.數據準備&#xff1a; 收集和處理數據&#xff0c;包括清洗、標準化和歸一化…

Unity_導航操作(鼠標控制人物移動)_運動動畫

文章目錄 前言一、Navigation 智能導航地圖烘焙1.創建Plan和NavMesh Surface2.智能導航地圖烘焙 二、MouseManager 鼠標控制人物移動1.給場景添加人物&#xff0c;并給人物添加導航組件2.編寫腳本管理鼠標控制3.給人物編寫腳本&#xff0c;訂閱事件&#xff08;添加方法給Mouse…

6. 接口分布式測試pytest-xdist

pytest-xdist實戰指南&#xff1a;解鎖分布式測試的高效之道 隨著測試規模擴大&#xff0c;執行時間成為瓶頸。本文將帶你深入掌握pytest-xdist插件&#xff0c;利用分布式測試將執行速度提升300%。 一、核心命令解析 加速安裝&#xff08;國內鏡像&#xff09; pip install …

預訓練語言模型

預訓練語言模型 1.1Encoder-only PLM ? Transformer結構主要由Encoder、Decoder組成&#xff0c;根據特點引入了ELMo的預訓練思路。 ELMo&#xff08;Embeddings from Language Models&#xff09;是一種深度上下文化詞表示方法&#xff0c; 該模型由一個**前向語言模型&…

Altera PCI IP target設計分享

最近調試也有關于使用Altera 家的PCI IP&#xff0c;然后分享一下代碼&#xff1a; 主要實現&#xff1a;主控作為主設備&#xff0c;FPGA作為從設備&#xff0c;主控對FPGA IO讀寫的功能 后續會分享FPGA作為主設備&#xff0c; 從 FPGA通過 memory寫到主控內存&#xff0c;會…

基于機器學習的智能文本分類技術研究與應用

在當今數字化時代&#xff0c;文本數據的爆炸式增長給信息管理和知識發現帶來了巨大的挑戰。從新聞文章、社交媒體帖子到企業文檔和學術論文&#xff0c;海量的文本數據需要高效地分類和管理&#xff0c;以便用戶能夠快速找到所需信息。傳統的文本分類方法主要依賴于人工規則和…

前端項目3-01:登錄頁面

一、效果圖 二、全部代碼 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>碼農魔盒</title><style>.bg{position: fixed;top: 0;left:0;object-fit: cover;width: 100vw;height: 100vh;}.box{width: 950px;he…

Nexus CLI:簡化你的分布式計算貢獻之旅

探索分布式證明網絡的力量&#xff1a;Nexus CLI 項目深入解析 在今天的數字時代&#xff0c;分布式計算和去中心化技術正成為互聯網發展的前沿。Nexus CLI 是一個為 Nexus 網絡提供證明的高性能命令行界面&#xff0c;它不僅在概念上先進&#xff0c;更是在具體實現中為開發者…

IBW 2025: CertiK首席商務官出席,探討AI與Web3融合帶來的安全挑戰

6月26日至27日&#xff0c;全球最大的Web3安全公司CertiK亮相伊斯坦布爾區塊鏈周&#xff08;IBW 2025&#xff09;&#xff0c;首席商務官Jason Jiang出席兩場圓桌論壇&#xff0c;分享了CertiK在AI與Web3融合領域的前沿觀察與安全見解。他與普華永道土耳其網絡安全服務主管Nu…

Vivado 五種仿真類型的區別

Vivado 五種仿真類型的區別 我們還是用“建房子”的例子來類比。您已經有了“建筑藍圖”&#xff08;HLS 生成的 RTL 代碼&#xff09;&#xff0c;現在要把它建成真正的房子&#xff08;FPGA 電路&#xff09;。這五種仿真就是在這個過程中不同階段的“質量檢查”。 1. 行為…

小程序快速獲取url link方法,短信里面快速打開鏈接

獲取小程序鏈接方法 uni.request({url:https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credential&appidwxxxxxxxxxxxx&secret111111111111111111111111111111111,method:GET,success(res) {console.log(res.data)let d {"path": "/xxx/…

Spring 框架(1-4)

第一章&#xff1a;Spring 框架概述 1.1 Spring 框架的定義與背景 Spring 是一個開源的輕量級 Java 開發框架&#xff0c;于 2003 年由 Rod Johnson 創立&#xff0c;旨在解決企業級應用開發的復雜性。其核心設計思想是面向接口編程和松耦合架構&#xff0c;通過分層設計&…

RabitQ 量化:既省內存又提性能

突破高維向量內存瓶頸:Mlivus Cloud RaBitQ量化技術的工程實踐與調優指南 作為大禹智庫高級研究員,擁有三十余年向量數據庫與AI系統架構經驗的我發現,在當今多模態AI落地的核心場景中,高維向量引發的內存資源消耗問題已成為制約系統規模化部署的“卡脖子”因素。特別是在大…

創客匠人:創始人 IP 打造的得力助手

在當今競爭激烈的商業環境中&#xff0c;創始人 IP 的打造對于企業的發展愈發重要。一個鮮明且具有影響力的創始人 IP&#xff0c;能夠為企業帶來獨特的競爭優勢&#xff0c;提升品牌知名度與美譽度。創客匠人在創始人 IP 打造過程中扮演著不可或缺的角色&#xff0c;為創始人提…