同學(xué)們好,今天我跟大家伙們聊一下關(guān)于 Spring JPA 的相關(guān)知識,首先我們來了解一下什么 JPA 。
1.什么是JPA
JPA
(Java Persistence API) 是 Sun
官方提出的 Java
持久化規(guī)范。它為 Java
開發(fā)人員提供了一種對象/關(guān)聯(lián)映射工具來管理 Java
應(yīng)用中的關(guān)系數(shù)據(jù)。他的出現(xiàn)主要是為了簡化現(xiàn)有的持久化開發(fā)工作和整合 ORM
技術(shù),結(jié)束現(xiàn)在 Hibernate
,TopLink
,JD
O 等 ORM
框架各自為營的凌亂局面。JPA
在充分吸收了現(xiàn)有Hibernate
,TopLink
,JDO
等ORM
框架的基礎(chǔ)上發(fā)展而來的,具有易于使用,伸縮性強等優(yōu)點。從上面的解釋中我們可以了解到JPA
是一套規(guī)范,而類似 Hibernate
,TopLink
,JDO
這些產(chǎn)品是實現(xiàn)了 JPA
規(guī)范。
了解了什么是 JPA
,我們來看看本文的主角——spring data jpa
。
2.spring data jpa
pring Data JPA
是 Spring
基于 ORM
框架、JPA
規(guī)范的基礎(chǔ)上封裝的一套 JPA
應(yīng)用框架,底層使用了 Hibernate
的 JPA
技術(shù)實現(xiàn),可使開發(fā)者用極簡的代碼即可實現(xiàn)對數(shù)據(jù)的訪問和操作。它提供了包括增刪改查等在內(nèi)的常用功能,且易于擴展!學(xué)習(xí)并使用 Spring Data JPA
可以極大提高開發(fā)效率。
什么意思呢?如果用過Hibernate
或者MyBatis
的話,就會知道對象關(guān)系映射(ORM)框架有多么方便。但是Spring Data JPA
框架功能更進(jìn)一步,為我們做了 一個數(shù)據(jù)持久層框架幾乎能做的任何事情。以Springboot
整合MyBatis
為例,比如我們要向數(shù)據(jù)庫中插入一些用戶的數(shù)據(jù),那么我們需要先定義用戶的實體類,然后我們要定義一個UserDao
:
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public int addUser(User user){
return jdbcTemplate.update("INSERT INTO t_user(username,jobs,phone) VALUE (?,?,?)",
user.getName,user.getJobs,user.getPhone);
}
public int updateUser(User user){
return jdbcTemplate.update("UPDATE t_user SET username=?,jobs=?,phone=? WHERE id=?",
user.getName,user.getJobs,user.getPhone,user.getId);
}
public int deleteUser(Integer id){
return jdbcTemplate.update("DELETE FROM t_user WHERE id=?",id);
}
public User getUserById(Integer id){
return jdbcTemplate.queryForObject("SELECT * FROM t_user WHERE id =?",new BeanPropertyRowMapper<>(User.class),id);
}
public List<User> getAllUser{
return jdbcTemplate.query("SELECT * FROM t_user",new BeanPropertyRowMapper<>(User.class));
}
}
以及UserService
@Service
public class UserService {
@Autowired
UserDao userDao;
public int addUser(User user){
return userDao.addUser(user);
}
public int updateUser(User user){
return userDao.updateUser(user);
}
public int deleteUser(Integer id){
return userDao.deleteUser(id);
}
public User getUserById(Integer id){
return userDao.getUserById(id);
}
public List<User> getAllUser{
return userDao.getAllUser;
}
}
最后,我們在去調(diào)用對應(yīng)的service
中的方法。這是傳統(tǒng)的方式,如果使用mapper
,會稍微簡單一些,比如我們要添加mapper
@Mapper
public interface UserMapper {
int addUser(User user);
int deleteUser(int id);
int updateUser(User user);
User getUserById(Integer id);
List<User> getAllUsers;
}
然后定義一個UserMapper.xml
,添加對應(yīng)的CURD SQL
語句,做好映射,然后改造service
,例如
@Service
public class UserService {
@Autowired
UserMapper userMapper;
public int addUser(User user){
return userMapper.addUser(user);
}
public int updateUser(User user){
return userMapper.updateUser(user);
}
public int deleteUser(Integer id){
return userMapper.deleteUser(id);
}
public User getUserById(Integer id){
return userMapper.getUserById(id);
}
public List<User> getAllUser{
return userMapper.getAllUsers;
}
}
發(fā)現(xiàn)什么問題了嗎?如果我們要去實現(xiàn)多個表的操作,我們需要定義不同的實體類,然后實現(xiàn)對應(yīng)的mapper
,然后寫同樣的增刪改查方法,最后調(diào)用。這也太麻煩了些,而Spring data jpa
則可以輕松的幫我們實現(xiàn)這些繁瑣重復(fù)且沒有技術(shù)含量的操作。我們一起看下吧!
3.案例演示
1.首先,我們需要配置pom.xml
<dependency>
????????<groupId>mysql</groupId>
????????<artifactId>mysql-connector-java</artifactId>
????</dependency>
????<dependency>
????????<groupId>org.springframework.boot</groupId>
????????<artifactId>spring-boot-starter-data-jpa</artifactId>
????</dependency>
2.然后是application.properties
的配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
這里重點簡單介紹下spring.jpa.properties.hibernate.hbm2ddl.auto
有幾種配置:
create
:表示每次加載Hibernate
時都會刪除上一次生成的表(包括數(shù)據(jù)),然后重新生成新表,即使兩次沒有任何修改也會這樣執(zhí)行。適用于每次執(zhí)行單測前清空數(shù)據(jù)庫的場景。
create-drop
:表示每次加載Hibernate
時都會生成表,但當(dāng)SessionFactory
關(guān)閉時,所生成的表將自動刪除。
update
:最常用的屬性值,第一次加載Hibernate
時創(chuàng)建數(shù)據(jù)表(前提是需要先有數(shù)據(jù)庫),以后加載Hibernate
時不會刪除上一次生成的表,會根據(jù)實體更新,只新增字段,不會刪除字段(即使實體中已經(jīng)刪除)。
validate
:每次加載Hibernate
時都會驗證數(shù)據(jù)表結(jié)構(gòu),只會和已經(jīng)存在的數(shù)據(jù)表進(jìn)行比較,根據(jù)model
修改表結(jié)構(gòu),但不會創(chuàng)建新表。
- 不配置此項,表示禁用自動建表功能
spring.jpa.show-sql=true
該配置當(dāng)在執(zhí)行數(shù)據(jù)庫操作的時候會在控制臺打印 sql
語句,方便我們檢查排錯等。
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
這是數(shù)據(jù)庫的方言配置。
3.接下來我們建立用戶實體類
@Entity
public?class?User?{
????@Id
????@GeneratedValue
????private?long?id;
????@Column(nullable?=?false,?unique?=?true)
????private?String?userName;
????@Column(nullable?=?false)
????private?String?password;
????@Column(nullable?=?false)
????private?int?age;
}
這里的一些注解解釋如下:
- @Entity 是一個類注解,用來注解該類是一個實體類用來進(jìn)行和數(shù)據(jù)庫中的表建立關(guān)聯(lián)關(guān)系,首次啟動項目的時候,默認(rèn)會在數(shù)據(jù)中生成一個同實體類相同名字的表(table),也可以通過注解中的
name
屬性來修改表(table)名稱, 如@Entity(name=“user”) , 這樣數(shù)據(jù)庫中表的名稱則是user
。該注解十分重要,如果沒有該注解首次啟動項目的時候你會發(fā)現(xiàn)數(shù)據(jù)庫沒有生成對應(yīng)的表。
- @Table 注解也是一個類注解,該注解可以用來修改表的名字,該注解完全可以忽略掉不用,@Entity 注解已具備該注解的功能。
- @Id 類的屬性注解,該注解表明該屬性字段是一個主鍵,該屬性必須具備,不可缺少。
- @GeneratedValue 該注解通常和 @Id 主鍵注解一起使用,用來定義主鍵的呈現(xiàn)形式,該注解通常有多種使用策略,先總結(jié)如下:
- @GeneratedValue(strategy= GenerationType.IDENTITY) 該注解由數(shù)據(jù)庫自動生成,主鍵自增型,在 mysql 數(shù)據(jù)庫中使用最頻繁,oracle 不支持。
- @GeneratedValue(strategy= GenerationType.AUTO)??主鍵由程序控制,默認(rèn)的主鍵生成策略,
oracle
默認(rèn)是序列化的方式,mysql
默認(rèn)是主鍵自增的方式。
- @GeneratedValue(strategy= GenerationType.SEQUENCE) 根據(jù)底層數(shù)據(jù)庫的序列來生成主鍵,條件是數(shù)據(jù)庫支持序列,
Oracle
支持,Mysql
不支持。
- @GeneratedValue(strategy= GenerationType.TABLE) 使用一個特定的數(shù)據(jù)庫表格來保存主鍵,較少使用。
上面這些主鍵生成策略中,以 mysql
為例,? IDENTITY ?
和 AUTO
用的較多,二者當(dāng)中IDENTIT
用的多些,以下文章當(dāng)中演示的 demo
主鍵均使用 @GeneratedValue(strategy= GenerationType.IDENTITY) 的生成策略。
@Column 是一個類的屬性注解,該注解可以定義一個字段映射到數(shù)據(jù)庫屬性的具體特征,比如字段長度,映射到數(shù)據(jù)庫時屬性的具體名字等。
@Transient ?是一個屬性注解,該注解標(biāo)注的字段不會被映射到數(shù)據(jù)庫當(dāng)中。
4.?聲明?UserRepository
接口,繼承JpaRepository
,如下所示
public interface UserRepository extends JpaRepository<User, Long> {
}
這里的 JpaRepository
繼承了接口PagingAndSortingRepository
和QueryByExampleExecutor
。而,PagingAndSortingRepository
又繼承CrudRepository
。
因此,JpaRepository
接口同時擁有了基本CRUD
功能以及分頁功能。因此,這里我們可以繼承JpaRepository
,從而獲得Spring
為我們預(yù)先定義的多種基本數(shù)據(jù)操作方法。
5.然后我們定義一個測試類,??這里我們演示下添加操作, @Transactional 表示開啟事務(wù)防止出現(xiàn)臟數(shù)據(jù)。
????????……
????????@Autowired
????private?UserRepository?userRepository;
????@Test
????@Transactional
????public?void?userAddTest()?{
????????User?user?=?new?User();
????????user.setUserName("吳彥祖");
????????user.setAge(30);
????????user.setPassword("123456");
????????userRepository.save(user);
????????User?item?=?userRepository.findByUserName("wyk");
????????log.info(JsonUtils.toJson(item));
????}
6.接下來我們說下查詢,查詢可以分為基本查詢和自定義查詢,一種是 spring data
默認(rèn)已經(jīng)實現(xiàn),只需要要繼承JpaRepository
,一種是根據(jù)查詢的方法來自動解析成 SQL
。
@Test
public?void?testQuery()?throws?Exception?{
????User?user=new?User();
????userRepository.findAll();
????userRepository.findOne(1l);
????userRepository.save(user);
????userRepository.delete(user);
????userRepository.count();
????userRepository.exists(1l);
????……
}
7.自定義的簡單查詢就是根據(jù)方法名來自動生成SQL
,主要的語法是findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy
后面跟屬性名稱,舉幾個例子:
User?findByUserName(String?userName);
User?findByUserNameOrEmail(String?username,?String?email);
Long?deleteById(Long?id);
Long?countByUserName(String?userName);
List<User>?findByEmailLike(String?email);
User?findByUserNameIgnoreCase(String?userName);
List<User>?findByUserNameOrderByEmailDesc(String?email);
8.接下來,我們說下復(fù)雜的查詢,在實際的開發(fā)中我們需要用到分頁、刪選、連表等查詢的時候就需要特殊的方法或者自定義 SQL
,以分頁查詢?yōu)槔?,分頁查詢在實際使用中非常普遍了,spring data jpa
已經(jīng)幫我們實現(xiàn)了分頁的功能,在查詢的方法中,需要傳入?yún)?shù)Pageable
,當(dāng)查詢中有多個參數(shù)的時候Pageabl
e建議做為最后一個參數(shù)傳入。Pageable
是 spring
封裝的分頁實現(xiàn)類,使用的時候需要傳入頁數(shù)、每頁條數(shù)和排序規(guī)則
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
9.我們看下下面的測試用例
@Test
public?void?testPageQuery()?throws?Exception?{
????int?page=1,size=5;
????Sort?sort?=?new?Sort(Direction.DESC,?"id");
????Pageable?pageable?=?new?PageRequest(page,?size,?sort);
????userRepository.findALL(pageable);
????userRepository.findByUserName("testName",?pageable);
}
Spring data
大部分的SQL
都可以根據(jù)方法名定義的方式來實現(xiàn),但是由于某些原因我們想使用自定義的SQL
來查詢,spring data
也是完美支持的,如下所示:
@Modifying
@Query("update?User?u?set?u.userName?=??1?where?c.id?=??2")
int?modifyByIdAndUserId(String??userName,?Long?id);
@Transactional
@Modifying
@Query("delete?from?User?where?id?=??1")
void?deleteByUserId(Long?id);
@Transactional(timeout?=?10)
@Query("select?u?from?User?u?where?u.emailAddress?=??1")
User?findByEmailAddress(String?emailAddress);
以上就是關(guān)于spring data jpa
的介紹。對spring
或者 Java
感興趣的同學(xué)可以看一下教程
Spring教程:http://hgci.cn/wkspring/
Spring微課:http://hgci.cn/minicourse/play/springcourse
Java教程:http://hgci.cn/java/
Java微課:http://hgci.cn/minicourse/play/javaminicourse
文章參考來源:www.toutiao.com/a6843247617955070471/