最近公司部分新開的項目需要使用Spring data JPA技術(shù),作為一個從來沒有用過JPA的小白來說,需要重新的學習。N年前簡單的看過JPA,這么多年沒用過,完全忘記了有木有。接下來的系列文章就一起來整理一下。
本次的spring data jpa系列文章都是以spring boot整合為基礎(chǔ)的。
Spring data JPA和Spring boot整合
Spring data JPA的介紹不說了,網(wǎng)上的說明很多,建議可以看一下程序員DD關(guān)于JPA的文章:Spring Boot中使用Spring-data-jpa讓數(shù)據(jù)訪問更簡單、更優(yōu)雅(為什么不直接轉(zhuǎn)載,畢竟自己寫出來的印象才會最深刻)。在整個篇幅中,也有很多內(nèi)容是借鑒這篇文章的。
引入的依賴
和Spring boot整合后的依賴很簡單。??
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
這里沒有指定版本號,用過Spring boot的都知道,在引入parent(spring-boot-starter-parent)依賴的時候已經(jīng)指定了版本號,后續(xù)再引入spring boot下的依賴就不需要指定版本號啦。
數(shù)據(jù)庫配置
這里寫的配置就是最簡單的連接方式,其他的細節(jié)配置不是本系列博客的討論點,就不多說了。
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
Spring data JPA配置
JPA的配置是比較多的,但是大部分保持默認即可,主要的見下面的配置列表:
spring.jpa.database = MYSQL #指定數(shù)據(jù)庫的類型
spring.jpa.show-sql = false #是否輸出SQL
spring.jpa.properties.hibernate.hbm2ddl.auto = false
spring.jpa.properties.hibernate.format_sql = true #是否格式化SQL語句
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect #指定方言
其中spring.jpa.properties.hibernate.hbm2ddl.auto是Hibernate的的相關(guān)配置,主要作用是:自動創(chuàng)建、更新、驗證數(shù)據(jù)庫表結(jié)構(gòu)。該參數(shù)的幾種配置如下:
-
create:每次加載hibernate時都會刪除上一次生成的表,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個重要原因。 -
create-drop:每次加載hibernate時根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動刪除。 -
update:最常用的屬性,第一次加載hibernate時根據(jù)model類會自動建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫),以后加載hibernate時根據(jù)model類自動更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器后,表結(jié)構(gòu)是不會被馬上建立起來的,是要等應用第一次運行起來后才會。 -
validate:每次加載hibernate時,驗證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),只會和數(shù)據(jù)庫中的表進行比較,不會創(chuàng)建新表,但是會插入新值。
在這里spring.jpa.properties.hibernate.hbm2ddl.auto使用的值是false,一開始使用的create-drop,但是每次運行都查不到結(jié)果,并且數(shù)據(jù)庫里面的表也被刪除了,好可怕。使用false直接關(guān)閉這個配置功能。
至此整合已經(jīng)完成了,沒有很多復雜的內(nèi)容,這就是使用了spring boot帶來的便利性,如果是spring mvc的話,還要通過xml配置或者Configuration配置類來設(shè)置JPA的相關(guān)屬性,過程要復雜的很多。
基本使用示例
實體類
在JPA中,指定數(shù)據(jù)表中的字段和實體類的字段映射關(guān)系是通過實體類中的注解實現(xiàn)的。
@Entity
@Table(name="t_user")
@EntityListeners({AuditingEntityListener.class})
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
public Integer id;
@Column(name="user_name")
public String userName;
@Column(name="user_phone")
public String userPhone;
@Column(name="age")
public Integer age;
@Column(name="address")
public String address;
}
-
@Entity注解是為了表明當前的的類是實體類 -
@Table指定當前實體類所對應的表名 -
@Id指定當前的字段是主鍵 -
@GeneratedValue指定注解的生成規(guī)則 -
@Column指定當前字段映射到數(shù)據(jù)中表的字段名(如果當前的這個字段是數(shù)據(jù)庫的關(guān)鍵字需要在name值上加"[]",如@Column(name="[index]"))
使用了@Entity注解后會默認映射到數(shù)據(jù)庫的表,按照駝峰規(guī)則,如類名是CustomerBaseInfo,那么就會對應到表名為customer_base_info表,此時就不需要@Table注解來指定表名了。另外就是@Column注解,是指定字段的映射關(guān)系,同樣的是可以通過駝峰規(guī)則自動匹配。
Repository接口類
在Mybatis中是定義Mapper接口類,在JPA中使用的是Repository接口,實現(xiàn)的方式和Mybatis中有所不同。
public class UserRepository extends JpaRepository<User,Integer>{
User findByUserName(@Param("userName")String userName);
@Query("select u from User u where u.userPhone=:userPhone")
User findUserByPhone(@Param("userPhone")String userPhone);
@Modifying
@Query("update User u set u.address=:address where u.id=:id")
int updateAddressById(@Param("address")String address,@Param("id")Integer id);
}
- 第一個方法
findByUserName是不需要寫對應的SQL語句的,JPA會根據(jù)方法名自動生成SQL,這就是JPA的強大之處; - 第二個方法通過
@Query注解,在注解中寫對應SQL,體現(xiàn)一下寫SQL的基本語法,注意的是JPA是面向?qū)ο蟮模磺卸际怯脤ο髮崿F(xiàn),包括表名也是用的直接實體類的名稱,JPA的SQL有一個專業(yè)的叫法是JPQL; - 第三個方法是修改表的記錄,需要注意的是在修改的時候需要加上
@Modifying注解,如果沒有這個注解會報錯的
關(guān)于JPA的各種實現(xiàn)方式有很多,表達式的寫法也有很多。后面更新文章將會細講。
單元測試
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {ItcrudJpaApplication.class})
public class JPATest {
@Autowired
private UserService userService;
@Test
public void findByUserNameTest() {
User user = userService.findByUserName("Joker");
System.out.println(user);
}
@Test
public void updateAddressByIdTest() {
int i = userService.updateAddressById("安徽六安", 1);
System.out.println("====>" + i);
}
@Test
public void findUserByPhoneTest(){
User user = userService.findUserByPhone("15611223344");
System.out.println(user);
}
}
在寫Junit測試的時候遇到兩個問題,下面提一下。
- JPA涉及到update、delete的時候必須在方法上加
@Transactional注解開啟事務,否則會拋出TransactionRequiredException異常。 - 接上面的問題,如果直接在test方法上加
@Transactional注解,會出現(xiàn)自動回滾的情況,也就是在執(zhí)行完整個test后,方法是自動回滾,這是test中的保護機制(之前不知道,現(xiàn)在掃個盲)。
簡單的整合,簡單的使用,對于一個正常業(yè)務系統(tǒng)來說,總是存在各種各樣的SQL,先埋個坑,后續(xù)更新跟上。
本文作者:程序猿楊鮑
版權(quán)歸作者所有,轉(zhuǎn)載請注明出處