什么是 JPA
大家好,今天我和大家聊一下關(guān)于Spring JPA 的相關(guān)知識(shí),我們先來(lái)了解下什么是 JPA ?
JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化規(guī)范。它為 Java 開(kāi)發(fā)人員提供了一種對(duì)象/關(guān)聯(lián)映射工具來(lái)管理 Java 應(yīng)用中的關(guān)系數(shù)據(jù)。他的出現(xiàn)主要是為了簡(jiǎn)化現(xiàn)有的持久化開(kāi)發(fā)工作和整合 ORM 技術(shù),結(jié)束現(xiàn)在 Hibernate,TopLink,JDO 等 ORM 框架各自為營(yíng)的凌亂局面。JPA 在充分吸收了現(xiàn)有 Hibernate,TopLink,JDO 等ORM框架的基礎(chǔ)上發(fā)展而來(lái)的,具有易于使用,伸縮性強(qiáng)等優(yōu)點(diǎn)。從上面的解釋中我們可以了解到JPA 是一套規(guī)范,而類似 Hibernate,TopLink,JDO 這些產(chǎn)品是實(shí)現(xiàn)了 JPA 規(guī)范。
了解了什么是 JPA,我們來(lái)看看本文的主角——spring data jpa。
spring data jpa
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 規(guī)范的基礎(chǔ)上封裝的一套 JPA 應(yīng)用框架,底層使用了 Hibernate 的 JPA 技術(shù)實(shí)現(xiàn),可使開(kāi)發(fā)者用極簡(jiǎn)的代碼即可實(shí)現(xiàn)對(duì)數(shù)據(jù)的訪問(wèn)和操作。它提供了包括增刪改查等在內(nèi)的常用功能,且易于擴(kuò)展!學(xué)習(xí)并使用 Spring Data JPA 可以極大提高開(kāi)發(fā)效率。
什么意思呢?如果用過(guò)Hibernate或者M(jìn)yBatis的話,就會(huì)知道對(duì)象關(guān)系映射(ORM)框架有多么方便。但是Spring Data JPA框架功能更進(jìn)一步,為我們做了 一個(gè)數(shù)據(jù)持久層框架幾乎能做的任何事情。以Springboot整合MyBatis為例,比如我們要向數(shù)據(jù)庫(kù)中插入一些用戶的數(shù)據(jù),那么我們需要先定義用戶的實(shí)體類,然后我們要定義一個(gè)UserDao:
@RepositorypublicclassUserDao{@Autowired JdbcTemplate jdbcTemplate;publicintaddUser(User user){return jdbcTemplate.update(“INSERT INTO t_user(username,jobs,phone) VALUE (?,?,?)”,user.getName(),user.getJobs(),user.getPhone()); }publicintupdateUser(User user){return jdbcTemplate.update(“UPDATE t_user SET username=?,jobs=?,phone=? WHERE id=?”, user.getName(),user.getJobs(),user.getPhone(),user.getId());}publicintdeleteUser(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
@ServicepublicclassUserService{@Autowired UserDao userDao;publicintaddUser(User user){return userDao.addUser(user);}publicintupdateUser(User user){return userDao.updateUser(user);}publicintdeleteUser(Integer id){return userDao.deleteUser(id); }public User getUserById(Integer id){return userDao.getUserById(id); }public List《User》 getAllUser(){return userDao.getAllUser(); }}
最后,我們?cè)谌フ{(diào)用對(duì)應(yīng)的service 中的方法。這是傳統(tǒng)的方式,如果使用mapper,會(huì)稍微簡(jiǎn)單一些,比如我們要添加mapper
@MapperpublicinterfaceUserMapper{intaddUser(User user);intdeleteUser(int id);intupdateUser(User user);User getUserById(Integer id);List《User》 getAllUsers();}
然后定義一個(gè)UserMapper.xml ,添加對(duì)應(yīng)的CURD SQL語(yǔ)句,做好映射,然后改造service,例如
@ServicepublicclassUserService{@Autowired UserMapper userMapper;publicintaddUser(User user){return userMapper.addUser(user);}publicintupdateUser(User user){return userMapper.updateUser(user);}publicintdeleteUser(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)什么問(wèn)題了嗎?如果我們要去實(shí)現(xiàn)多個(gè)表的操作,我們需要定義不同的實(shí)體類,然后實(shí)現(xiàn)對(duì)應(yīng)的mapper,然后寫(xiě)同樣的增刪改查方法,最后調(diào)用。這也太麻煩了些,而Spring data jpa 則可以輕松的幫我們實(shí)現(xiàn)這些繁瑣重復(fù)且沒(méi)有技術(shù)含量的操作。我們一起看下吧!
案例演示
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=truespring.datasource.username=rootspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.jpa.properties.hibernate.hbm2ddl.auto=createspring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialectspring.jpa.show-sql=true
這里重點(diǎn)簡(jiǎn)單介紹下spring.jpa.properties.hibernate.hbm2ddl.auto有幾種配置:
create:表示每次加載Hibernate時(shí)都會(huì)刪除上一次生成的表(包括數(shù)據(jù)),然后重新生成新表,即使兩次沒(méi)有任何修改也會(huì)這樣執(zhí)行。適用于每次執(zhí)行單測(cè)前清空數(shù)據(jù)庫(kù)的場(chǎng)景。
create-drop:表示每次加載Hibernate時(shí)都會(huì)生成表,但當(dāng)SessionFactory關(guān)閉時(shí),所生成的表將自動(dòng)刪除。
update:最常用的屬性值,第一次加載Hibernate時(shí)創(chuàng)建數(shù)據(jù)表(前提是需要先有數(shù)據(jù)庫(kù)),以后加載Hibernate時(shí)不會(huì)刪除上一次生成的表,會(huì)根據(jù)實(shí)體更新,只新增字段,不會(huì)刪除字段(即使實(shí)體中已經(jīng)刪除)。
validate:每次加載Hibernate時(shí)都會(huì)驗(yàn)證數(shù)據(jù)表結(jié)構(gòu),只會(huì)和已經(jīng)存在的數(shù)據(jù)表進(jìn)行比較,根據(jù)model修改表結(jié)構(gòu),但不會(huì)創(chuàng)建新表。
不配置此項(xiàng),表示禁用自動(dòng)建表功能
spring.jpa.show-sql=true 該配置當(dāng)在執(zhí)行數(shù)據(jù)庫(kù)操作的時(shí)候會(huì)在控制臺(tái)打印 sql 語(yǔ)句,方便我們檢查排錯(cuò)等。
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect 這是數(shù)據(jù)庫(kù)的方言配置。
3.接下來(lái)我們建立用戶實(shí)體類
@EntitypublicclassUser{@Id@GeneratedValueprivatelong id;@Column(nullable = false, unique = true)private String userName;@Column(nullable = false)private String password;@Column(nullable = false)privateint age;}
這里的一些注解解釋如下:
@Entity 是一個(gè)類注解,用來(lái)注解該類是一個(gè)實(shí)體類用來(lái)進(jìn)行和數(shù)據(jù)庫(kù)中的表建立關(guān)聯(lián)關(guān)系,首次啟動(dòng)項(xiàng)目的時(shí)候,默認(rèn)會(huì)在數(shù)據(jù)中生成一個(gè)同實(shí)體類相同名字的表(table),也可以通過(guò)注解中的 name 屬性來(lái)修改表(table)名稱, 如@Entity(name=“user”) , 這樣數(shù)據(jù)庫(kù)中表的名稱則是 user 。該注解十分重要,如果沒(méi)有該注解首次啟動(dòng)項(xiàng)目的時(shí)候你會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)沒(méi)有生成對(duì)應(yīng)的表。
@Table 注解也是一個(gè)類注解,該注解可以用來(lái)修改表的名字,該注解完全可以忽略掉不用,@Entity 注解已具備該注解的功能。
@Id 類的屬性注解,該注解表明該屬性字段是一個(gè)主鍵,該屬性必須具備,不可缺少。
@GeneratedValue 該注解通常和 @Id 主鍵注解一起使用,用來(lái)定義主鍵的呈現(xiàn)形式,該注解通常有多種使用策略,先總結(jié)如下:
@GeneratedValue(strategy= GenerationType.IDENTITY) 該注解由數(shù)據(jù)庫(kù)自動(dòng)生成,主鍵自增型,在 mysql 數(shù)據(jù)庫(kù)中使用最頻繁,oracle 不支持。
@GeneratedValue(strategy= GenerationType.AUTO) 主鍵由程序控制,默認(rèn)的主鍵生成策略,oracle 默認(rèn)是序列化的方式,mysql 默認(rèn)是主鍵自增的方式。
@GeneratedValue(strategy= GenerationType.SEQUENCE) 根據(jù)底層數(shù)據(jù)庫(kù)的序列來(lái)生成主鍵,條件是數(shù)據(jù)庫(kù)支持序列,Oracle支持,Mysql不支持。
@GeneratedValue(strategy= GenerationType.TABLE) 使用一個(gè)特定的數(shù)據(jù)庫(kù)表格來(lái)保存主鍵,較少使用。
上面這些主鍵生成策略中,以 mysql 為例, IDENTITY 和 AUTO 用的較多,二者當(dāng)中 IDENTITY 用的多些,以下文章當(dāng)中演示的 demo 主鍵均使用 @GeneratedValue(strategy= GenerationType.IDENTITY) 的生成策略。
@Column 是一個(gè)類的屬性注解,該注解可以定義一個(gè)字段映射到數(shù)據(jù)庫(kù)屬性的具體特征,比如字段長(zhǎng)度,映射到數(shù)據(jù)庫(kù)時(shí)屬性的具體名字等。
@Transient 是一個(gè)屬性注解,該注解標(biāo)注的字段不會(huì)被映射到數(shù)據(jù)庫(kù)當(dāng)中。
4. 聲明 `UserRepository`接口,繼承`JpaRepository`,如下所示
publicinterfaceUserRepositoryextendsJpaRepository《User, Long》 {}
這里的 JpaRepository繼承了接口PagingAndSortingRepository和QueryByExampleExecutor。而,PagingAndSortingRepository又繼承CrudRepository。
因此,JpaRepository接口同時(shí)擁有了基本CRUD功能以及分頁(yè)功能。因此,這里我們可以繼承JpaRepository,從而獲得Spring為我們預(yù)先定義的多種基本數(shù)據(jù)操作方法。
5.然后我們定義一個(gè)測(cè)試類, 這里我們演示下添加操作, @Transactional 表示開(kāi)啟事務(wù)防止出現(xiàn)臟數(shù)據(jù)。
……@Autowiredprivate UserRepository userRepository;@Test@TransactionalpublicvoiduserAddTest(){ 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.接下來(lái)我們說(shuō)下查詢,查詢可以分為基本查詢和自定義查詢,一種是 spring data 默認(rèn)已經(jīng)實(shí)現(xiàn),只需要要繼承`JpaRepository`,一種是根據(jù)查詢的方法來(lái)自動(dòng)解析成 SQL。
@TestpublicvoidtestQuery()throws Exception { User user=new User();userRepository.findAll(); userRepository.findOne(1l); userRepository.save(user);userRepository.delete(user); userRepository.count(); userRepository.exists(1l); ……}
7.自定義的簡(jiǎn)單查詢就是根據(jù)方法名來(lái)自動(dòng)生成SQL,主要的語(yǔ)法是`findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy`后面跟屬性名稱,舉幾個(gè)例子:
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.接下來(lái),我們說(shuō)下復(fù)雜的查詢,在實(shí)際的開(kāi)發(fā)中我們需要用到分頁(yè)、刪選、連表等查詢的時(shí)候就需要特殊的方法或者自定義 SQL,以分頁(yè)查詢?yōu)槔猪?yè)查詢?cè)趯?shí)際使用中非常普遍了,spring data jpa已經(jīng)幫我們實(shí)現(xiàn)了分頁(yè)的功能,在查詢的方法中,需要傳入?yún)?shù)Pageable,當(dāng)查詢中有多個(gè)參數(shù)的時(shí)候Pageable建議做為最后一個(gè)參數(shù)傳入。Pageable是 spring 封裝的分頁(yè)實(shí)現(xiàn)類,使用的時(shí)候需要傳入頁(yè)數(shù)、每頁(yè)條數(shù)和排序規(guī)則
Page《User》 findALL(Pageable pageable);Page《User》 findByUserName(String userName,Pageable pageable);
9.我們看下下面的測(cè)試用例
@TestpublicvoidtestPageQuery()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);}
10. Spring data 大部分的 SQL 都可以根據(jù)方法名定義的方式來(lái)實(shí)現(xiàn),但是由于某些原因我們想使用自定義的 SQL 來(lái)查詢,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);
-
JAVA
+關(guān)注
關(guān)注
20文章
2987瀏覽量
107237 -
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14926 -
JAVA開(kāi)發(fā)
+關(guān)注
關(guān)注
0文章
15瀏覽量
7675
發(fā)布評(píng)論請(qǐng)先 登錄
Spring Boot Starter需要些什么

Spring事務(wù)失效的十種常見(jiàn)場(chǎng)景
java spring教程
什么是java spring
如何用ACM簡(jiǎn)化你的Spring Cloud微服務(wù)環(huán)境配置管理
使用阿里云ACM簡(jiǎn)化你的Spring Cloud微服務(wù)環(huán)境配置管理
一文詳談波峰焊接工藝資料下載

Spring認(rèn)證_什么是Spring GraphQL

Spring認(rèn)證_什么是Spring GraphQL?

使用JPA訪問(wèn)數(shù)據(jù)的過(guò)程
JPA基礎(chǔ)概念

評(píng)論