如今User表已經有了,並且對應mysql數據庫裏面,已經建好了用戶表。
我琢磨着不是要作登陸功能嘛,那就得先往User表裏頭添加一條數據啊。用mysql front直接添加確定是能夠的,不過前段時間正好看了兔子發在B站的SSM商城系統,裏面好像有個地方可以直接用Junit Test測試的,雖然這個系統不是SSM,不過應該也能夠吧。java
對了,pom.xml裏面不是有這麼一段配置嘛:mysql
<!-- springboot test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
這玩意怎麼看都像是測試用的依賴啊,這是否是意味着,我就不用跟視頻裏面那樣,去引入jar包了??web
嗯,確定是的。spring
OK,說幹就幹,建立一個測試包和測試類:
sql
package com.edu.test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; @RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration public class UserTest { }
打上註解,額,對了,我TM好像還沒寫dao方法呢,趕忙去寫個。
建立一個dao包,這個包裏面都放持久層的類,如今添加一個UserDao的接口。兔子關於SpringBoot的文章裏面已經寫過怎麼使用JPA了,這邊依葫蘆畫瓢。
直接寫一個UserDao接口,繼承一下JPA,注意,包別導錯了。數據庫
package com.edu.dao; import org.springframework.data.jpa.repository.JpaRepository; import com.edu.entity.User; public interface UserDao extends JpaRepository<User, String>{ }
這樣就ok了,而後,回到測試類,把這個接口注入進去。
springboot
add測試方法框架
@Test public void addUser(){ User user = new User(); user.setUserName("root"); user.setPassword("root"); user.setCreateTime("20210103"); user.setNickName("剽悍一小兔"); user.setRoleId("1");//默認1是管理員 user.setIsDelete("0");//默認不刪除 user.setIsLogined("0");//默認沒有登陸 userDao.save(user); System.out.println("保存成功!"); }
開始測試:
spring-boot
哇,真的好了嘛,趕忙看下數據庫??單元測試
OK了,真的來了。
我葉小凡居然也能夠觸類旁通啦,兔子還沒出這個SpringBoot版本的測試教程呢,我就憑藉本身驚人的天賦,提早搞定了,哈哈哈。
兔子:「你這個單元測試還能夠優化哦,親~」
「啥狀況,這不是很完美嘛?」
兔子:「這只是第一個單元測試,後面可能還會有不少其餘的單元測試,你能夠作一個通用的父類,這樣就不用在每一個測試類上打那麼多註解了。」
因而,在兔子的指導下,我雖然不服,但仍是照作了。
這樣,在測試包下面,咱們建立一個通用的測試父類。
package com.edu.test; import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; @RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration public class BaseTest { @Before public void init() { System.out.println("開始測試-----------------"); } @After public void after() { System.out.println("測試結束-----------------"); } }
而後,UserTest就繼承這個父類,不用再加測試的註解了。
public class UserTest extends BaseTest{ }
驗證一下,咱們再寫個測試方法,把剛纔的數據刪掉。
使用jpa進行update操做主要有兩種方式:
一、調用保存實體的方法
1)保存一個實體:repository.save(T entity)
2)保存多個實體:repository.save(Iterable
3)保存並當即刷新一個實體:repository.saveAndFlush(T entity)
注:如果更改,entity中必須設置了主鍵字段,否則不能對應上數據庫中的記錄,變成新增(數據庫自動生成主鍵)或報錯(數據庫不自動生成主鍵)了
二、@Query註解,本身寫JPQL語句
@Modifying @Query("update ShopCoupon sc set sc.deleted = true where sc.id in :ids") public void deleteByIds(@Param(value = "ids") List<String> ids);
1)update或delete時必須使用@Modifying對方法進行註解,才能使得ORM知道如今要執行的是寫操做
2)有時候不加@Param註解參數,可能會報以下異常:
org.springframework.dao.InvalidDataAccessApiUsageException: Name must not be null or empty!; nested exception i is Java.lang.IllegalArgumentException: Name must not be null or empty!
以上資料摘自百度,哈哈,我該用哪種呢?第二種方法比較親切,直接用sql語句了,那就使用第二種吧。
按照百度到的說法,先在dao增長一個方法,本身寫jpql語句,其實我也不太懂啥叫jpql語句,估計意思就是正常寫sql,可是呢,字段的名字和User類裏面的字段保持一致就好了。由於我發現,生成的表,仍是用了下劃線,是這樣的:
因而,我就不能用下劃線。
public interface UserDao extends JpaRepository<User, String>{ @Modifying @Query("update User u set u.isDelete = 1 where u.userName = :userName") public void deleteByUsername(@Param(value = "userName") String userName); }
新的測試方法,我要經過userName去作刪除,刪除不是真的刪除,而是邏輯刪除。
@Test public void deleteUser1(){ userDao.deleteByUsername("root"); System.out.println("刪除成功"); }
運行,就報錯了:
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query at ... ...
我靠,啥狀況,我百度到的啊,怎麼會錯呢。
算了算了,這個不行,就換另外一種方法。
兔子:「小夥子,你這樣可不行啊,你好歹看下報啥錯啊。。。」
「額,好吧,我看下哈!咦,這個好熟悉,TransactionRequiredException,Transaction這個單詞的意思好像是那個事物吧。Required是須要的意思,莫非報錯的意思是,讓我加一個事物,是這樣嘛?」
兔子:「別問我啊,你本身試一下不就知道了嘛!」
「好吧,我就加一個事物的註解看看。奇怪了,我明明百度的文章,哎。」
兔子:「事物通常是加在service方法裏面的,你別加在dao裏面啊。你想直接測試dao層的方法,這個想法沒有錯,不過你最好仍是弄個service。」
「你的意思是,我再加一個service方法,加上事物,而後調用dao的方法?」
兔子:「嗯,或者你直接把事物加在test方法,也行的。」
說罷,兔哥幫我加上了註解,而後測試,居然經過了。
「這麼說,我找的那篇文章,其實也是對的,等下奧,我翻下連接。」
兔子:「嗯,我看看。」
「就是這個...」
地址:https://blog.csdn.net/qq_33405420/article/details/89469293
兔子:「這寫的沒問題啊,只是別人不知道你這麼菜而已,他也不知道你直接在junit測試類裏面去測試dao的方法,並且還不加事物。」
「。。。好吧,那我後面創建service方法的時候,必定加上事物。」
兔子:「沒事,我剛開始也這樣,慢慢來就行了。對了,我已經把公衆號的名字改爲了【java小白翻身】,記得關注哦~」
話說這JPA還真好用,基本的增刪改查我都不用寫一句sql,對於一些複雜的業務邏輯,我也能夠本身寫jpql語句(其實仍是sql語句,算是面向對象的sql語句吧)。
接下來,我就試試別的方法。剛纔已經插入了一條數據,如今我再用另外一種方法去修改數據,好比,我把密碼改爲123吧。
@Test public void modifyPassword(){ User user = new User(); user.setUserName("root"); user.setPassword("123"); userDao.saveAndFlush(user); System.out.println("修改爲功"); }
運行,結果崩了...
其餘的數據全沒了,看來這種更新是全量的更新,不是增量的。我還覺得他會只更新userName和password呢,看來是我太天真了。不怕,我再運行一下adduser測試方法,數據不就回來了嘛。。
我太機智了。
再運行一次,數據果真回來了。
這回得當心一點了,我先根據主鍵userName去拿到這個用戶,而後再修改密碼:
@Test public void modifyPassword(){ User user = new User(); user.setUserName("root"); //先找到userName爲root的用戶 user = userDao.findOne("root"); //修改密碼 user.setPassword("123"); userDao.saveAndFlush(user); System.out.println("修改爲功"); }
這下子就成功了。
springDataJpa還實現了一個很是牛逼的東西,就是根據方法名自動進行sql查詢。
好比,我想根據roleId去作查詢,就能夠直接寫一個方法:
public List<User> findByRoleId(String roleId);
顧名思義,方法命名規則查詢就是根據方法的名字,就能建立查詢。
只須要按照Spring Data JPA提供的方法命名規則定義方法的名稱,就能夠完成查詢工做。
Spring Data JPA在程序執行的時候會根據方法名稱進行解析,並自動生成查詢語句進行查詢
按照Spring Data JPA 定義的規則,查詢方法以findBy開頭,涉及條件查詢時,條件的屬性用條件關鍵字鏈接,
要注意的是:條件屬性首字母需大寫。框架在進行方法名解析時,會先把方法名多餘的前綴截取掉,而後對剩下部分進行解析。
咱們多造一點測試數據,用addUser方法。
好比,咱們如今要查詢nickName裏面帶有「剽悍」的,就用like。
public List<User> findByNickNameLike(String nickName);
測試:
@Test public void userQuery(){ List<User> users = userDao.findByNickNameLike("%剽悍%"); for (int i = 0; i < users.size(); i++) { System.out.println(users.get(i).getNickName()); } }
結果:
這個算是jpa裏面一個頗有意思的用法了,可是我感受這樣內心好沒底啊,哈哈。最穩妥的辦法,仍是直接寫JPQL語句吧。
好比,我這樣寫:
@Query("select u from User u where userName = ?1 and password = ?2") public User findByUserNameAndPassword(String userName,String password);
這樣的好處就是,你想寫什麼查詢就寫什麼查詢,是最放心的。
JPA裏面最經常使用的兩種傳參方式,就是這兩種。
其實,jpa裏面還有不少其餘的拼接方法,可是我看來看去,都太麻煩了,仍是直接用JPQL最省事。對於簡單的查詢,就直接用默認的方法便可,複雜的查詢,就老老實實本身寫sql吧。