SpringBoot整合JPA
使用数据库是开发基本应用的基础,借助于开发框架,我们已经不用编写原始的访问数据库的代码,也不用调用JDBC(Java Data Base Connectivity)或者连接池等诸如此类的被称作底层的代码,我们将从更高的层次*问数据库,这在Springboot中更是如此,本章我们将详细介绍在Springboot中使用 Spring Data JPA 来实现对数据库的操作。
JPA & Spring Data JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是Sun官方提出的Java持久化规范.
其设计目标主要是为了简化现有的持久化开发工作和整合ORM技术。
JPA使用XML文件或注解(JDK 5.0或更高版本)来描述对象-关联表的映射关系,能够将运行期的实体对象持久化到数据库,它为Java开发人员提供了一种ORM工具来管理Java应用中的关系数据。
简单地说,JPA就是为POJO(Plain Ordinary Java Object)提供持久化的标准规范,即将Java的普通对象通过对象关系映射(Object-Relational Mapping,ORM)持久化到数据库中。
由于JPA是在充分吸收了现有Hibernate,TopLink,JDO等ORM框架的基础上发展而来的,因而具有易于使用、伸缩性强等优点。
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 规范的基础上开发的一个框架,使用 Spring Data JPA 可以极大地简化JPA 的写法,可以在几乎不用写实现的情况下实现对数据库的访问和操作,除了CRUD外,还包括分页和排序等一些常用的功能。
Spring Data JPA 还提供了对分页查询、自定义SQL、查询指定N条记录、联表查询等功能的支持
JPA不是一种新的ORM框架,它的出现只是用于规范现有的ORM技术,它不能取代现有的Hibernate、TopLink等框架。相反,在采用JPA开发时,我们将仍将使用到这些ORM框架,只是此时开发出来的应用不再依赖于某个持久化提供商。应用可以在不修改代码的情况下在任何JPA环境下运行,真正做到低耦合,可扩展的程序设计。
Hibernate & JPA
1、JPA
全称Java Persistence API,通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
JPA的出现有两个原因:
其一,简化现有Java EE和Java SE应用的对象持久化的开发工作;
其二,Sun希望整合对ORM技术,实现持久化领域的统一。
JPA提供的技术:
1)ORM映射元数据:JPA支持XML和JDK 5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
2)JPA 的API:用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
3)查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
2、JPA & Hibernate 关系
JPA是规范,Hibernate是框架,JPA是持久化规范,而Hibernate实现了JPA。
Hibernate VS Mybatis
Mybatis:小巧、方便、高效、简单、直接、半自动
Hibernate:强大、方便、高效、复杂、绕弯子、全自动
一、导入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
< dependencies >
< dependency >
< groupId >com.alibaba</ groupId >
< artifactId >fastjson</ artifactId >
< version >1.2.76</ version >
</ dependency >
< dependency >
< groupId >org.projectlombok</ groupId >
< artifactId >lombok</ artifactId >
</ dependency >
< dependency >
< groupId >com.alibaba</ groupId >
< artifactId >druid</ artifactId >
< version >1.1.21</ version >
</ dependency >
< dependency >
< groupId >org.springframework.boot</ groupId >
< artifactId >spring-boot-starter-data-jpa</ artifactId >
</ dependency >
< dependency >
< groupId >org.springframework.boot</ groupId >
< artifactId >spring-boot-starter-web</ artifactId >
</ dependency >
< dependency >
< groupId >mysql</ groupId >
< artifactId >mysql-connector-java</ artifactId >
< scope >runtime</ scope >
</ dependency >
< dependency >
< groupId >org.springframework.boot</ groupId >
< artifactId >spring-boot-starter-test</ artifactId >
< scope >test</ scope >
</ dependency >
</ dependencies >
|
二、简单的CRUD
1、配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/shy
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
jpa:
hibernate:
#定义数据库表的生成策略 create 创建一个表 update 更新或者创建数据表
ddl-auto: update
#控制台显示sql语句
show-sql: true
server:
port: 80
|
其中,spring.jpa.hibernate.ddl-auto 参数用来配置是否开启自动更新数据库表结构,可取create、create-drop、update、validate、none五个值。
- create 每次加载hibernate时,先删除已存在的数据库表结构再重新生成;
- create-drop 每次加载hibernate时,先删除已存在的数据库表结构再重新生成,并且当 sessionFactory关闭时自动删除生成的数据库表结构;
- update 只在第一次加载hibernate时自动生成数据库表结构,以后再次加载hibernate时根据model类自动更新表结构;
- validate 每次加载hibernate时,验证数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
- none 关闭自动更新
2、实体类
Shop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.shy.entity;
import lombok.Data;
import javax.persistence.*;
@Data
//使用jpa注解 配置映射关系
@Entity //告诉jpa这是一个实体类 和数据表映射的类
public class Shop{
@Id //表明这是以一个主键
@GeneratedValue (strategy = GenerationType.IDENTITY) //自增
private Integer shopId;
@Column (length = 20 )
private String shopName;
private double price;
private Integer shopClassId;
private Integer num;
}
|
ShopClass,商品类别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.shy.entity;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@Entity
public class ShopClass {
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Integer shopClassId;
private String shopClassName;
}
|
3、Dao层
编写dao继承JpaRepository类,泛型传入 要操作的实体类,和主键类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.example.dao;
import com.shy.entity.Shop;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/* 参数一 T :当前需要映射的实体
* 参数二 ID :当前映射的实体中的OID(映射对象标识符,数据库主键)的类型*/
@Repository
public interface ShopRepository extends JpaRepository<Shop,Integer> {
/*
* 我们在这里直接继承 JpaRepository
* 这里面已经有很多现成的方法了
* 这也是JPA的一大优点
* 我们可以直接使用这些方法,包括其父类的好多方法。
* */
}
|
JpaRepository:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package org.springframework.data.jpa.repository;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
@Override
List<T> findAll(); // 查询所有实体
@Override
List<T> findAll(Sort sort); // 查询所有实体并排序
@Override
List<T> findAllById(Iterable<ID> ids); // 根据ID集合查询实体
@Override
<S extends T> List<S> saveAll(Iterable<S> entities); // 保存并返回(修改后的)实体集合
void flush(); // 提交事务
<S extends T> S saveAndFlush(S entity); // 保存实体并立即提交事务
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
@Deprecated
default void deleteInBatch(Iterable<T> entities){deleteAllInBatch(entities);}
void deleteAllInBatch(Iterable<T> entities); // 批量删除实体集合
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch(); // 批量删除所有实体
@Deprecated
T getOne(ID id); // 根据ID查询实体
T getById(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example); // 查询与指定Example匹配的所有实体
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort); // 查询与指定Example匹配的所有实体并排序
}
|
4、service层
service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.shy.service;
import com.shy.entity.Shop;
import com.shy.vo.ShopAndShopClassVo;
import java.util.List;
public interface ShopService {
//查询所有商品
List<Shop> findAll();
//增加商品
Shop addShop(Shop shop);
//通过商品id修改商品名
Shop updateShop();
//通过商品id删除商品
void delShop(Integer id);
}
|
Impl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.shy.service;
import com.shy.dao.ShopRepository;
import com.shy.entity.Shop;
import com.shy.vo.ShopAndShopClassVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ShopServiceImpl implements ShopService{
@Autowired
private ShopRepository shopRepository;
@Override
public List<Shop> findAll() {
return shopRepository.findAll();
}
@Override
public Shop addShop(Shop shop) {
shop.setPrice( 333 );
shop.setShopClassId( 3 );
shop.setNum( 30 );
shop.setShopName( "耳机" );
return shopRepository.save(shop);
}
@Override
public Shop updateShop() {
Shop shop = new Shop();
shop.setShopId( 11 );
shop.setShopName( "平板" );
shop.setShopClassId( 3 );
shop.setNum( 40 );
return shopRepository.save(shop);
}
@Override
public void delShop(Integer id) {
shopRepository.deleteById(id);
System.out.println( "删除成功" );
}
}
|
5、controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.shy.controller;
import com.alibaba.fastjson.JSON;
import com.shy.entity.Shop;
import com.shy.service.ShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ShopController {
@Autowired
private ShopService shopService;
@GetMapping ( "/list" )
public String findAll(){
return JSON.toJSONString(shopService.findAll());
}
@GetMapping ( "/save" )
public String save(Shop shop){
return JSON.toJSONString(shopService.addShop(shop));
}
@GetMapping ( "/saveAndFlush" )
public String saveAndFlush(){
return JSON.toJSONString(shopService.updateShop());
}
@GetMapping ( "/delShop/{id}" )
public void delShop( @PathVariable Integer id){
shopService.delShop(id);
}
}
|
全程用postman测试
三、自定义SQL
在ShopRepository中添加
1
2
3
4
5
6
7
8
9
|
//使用原生sql需要加上,nativeQuery = true
//?1代表第一个参数
@Query (value= "select * from shop where shop.price = ?1" ,nativeQuery = true )
Shop findByPrice(Double price);
//修改商品,@Modifying+@Query执行更新操作,serviceImpl不要忘记加上,要使用hql的话,需要把entity别名删掉
@Transactional //设计修改表的操作需要开启事务支持
@Modifying //这个注解只支持返回值为int/Integer
@Query ( "update Shop s set s.shopName = ?1 where s.shopId = ?2" )
int updateshop2(String name,Integer shopId);
|
service:
1
2
3
4
|
//通过价格查询商品
Shop findByPrice(Double price);
//修改商品名原生sql方法
int updateshop2(String name,Integer shopId);
|
Impl:
1
2
3
4
5
6
7
8
|
@Override
public Shop findByPrice(Double price) {
return shopRepository.findByPrice(price);
}
@Override
public int updateshop2(String name, Integer shopId) {
return shopRepository.updateshop2(name, shopId);
}
|
controller:
1
2
3
4
5
6
7
8
9
|
@GetMapping ( "/listPrice/{price}" )
public String findByPrice( @PathVariable Double price){
return JSON.toJSONString(shopService.findByPrice(price));
}
@GetMapping ( "/saveAndFlush2/{id}/{name}" )
public String saveAndFlush2( @PathVariable (value = "id" ) Integer shopId,
@PathVariable String name){
return shopService.updateshop2(name, shopId)> 0 ? "修改成功" : "修改失败" ;
}
|
四、分页查询
Pageable 是 Spring 封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则。
Spring Data JPA 已经帮我们内置了分页功能,在查询的方法中,需要传入参数 Pageable,当查询中有多个参数的时候 Pageable 建议作为最后一个参数传入,
Pageable使用的时候需要传入页数、每页条数和排序规则,排序规则可省略
service:
1
2
3
4
5
6
7
8
|
/**
* 分页查询
* @param pageNo 第几页
* @param pageSize 每页有多少条数据
* @param pageable Spring 封装的分页实现类
* @return 数据
*/
Page<Shop> pageShop(Integer pageNo,Integer pageSize,Pageable pageable);
|
Impl:
1
2
3
4
5
6
7
|
@Override
public Page<Shop> pageShop(Integer pageNo, Integer pageSize, Pageable pageable) {
//注意排序这找的是实体类中的字段,不是数据库里的
//分页页码从0开始
pageable = PageRequest.of(pageNo,pageSize, Sort.Direction.DESC, "shopId" );
return shopRepository.findAll(pageable);
}
|
controller:
1
2
3
4
5
|
@GetMapping ( "/pageShop/{pageNo}/{pageSize}" )
public String pageShop( @PathVariable Integer pageNo,
@PathVariable Integer pageSize,Pageable pageable){
return JSON.toJSONString(shopService.pageShop(pageNo, pageSize, pageable));
}
|
五、连表查询
VO(value object)值对象
- 通常用于业务层之间的数据传递,仅仅包含数据而已
- View object:视图对象
接受页面传递来的对象,封装对象
将业务处理完成的对象,封装成页面要用的数据
创建ShopAndShopClassVo用来接收连表查询后的结果
entity,这里的有参构造必须加,要往里赋值
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.shy.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ShopAndShopClassVo {
private String shopName;
private double price;
private String shopClassName;
}
|
ShopRepository:
1
2
3
4
|
//连表查询
@Query ( "select new com.shy.vo.ShopAndShopClassVo(s.shopName,s.price,sc.shopClassName) from Shop s,ShopClass sc " +
"where s.shopClassId=sc.shopClassId and sc.shopClassName=?1" )
List<ShopAndShopClassVo> findShopInfo(String shopClassName);
|
JPQL进行查询,它的特征就是与原生SQL语句类似,完全面向对象,通过类名和属性访问,而不是表名和表属性
service:
1
2
|
//连表查询
List<ShopAndShopClassVo> findShopInfo(String shopClassName);
|
Impl:
1
2
3
4
|
@Override
public List<ShopAndShopClassVo> findShopInfo(String shopClassName) {
return shopRepository.findShopInfo(shopClassName);
}
|
controller:
1
2
3
4
|
@GetMapping ( "/andFind/{name}" )
public String findShopInfo( @PathVariable ( "name" ) String shopClassName){
return JSON.toJSONString(shopService.findShopInfo(shopClassName));
}
|
六、分组查询
接收数据的另一种写法,创建一个结果集的接口来接收连表查询后的结果
定义一个结果集的接口类,接口类的内容来自于商品表和商品类别表。
entity:
1
2
3
4
5
|
package com.shy.entity;
public interface GroupShop {
String getNum();
String getShopClassName();
}
|
在运行中 Spring 会给接口(GroupShop)自动生产一个代理类来接收返回的结果,代码中使用 getXX 的形式来获取。
ShopRepository:
1
2
3
|
//分组查询
@Query ( "select count(s.shopName) as 商品数量,sc.shopClassName as 类别名称 from Shop s,ShopClass sc where s.shopClassId=sc.shopClassId group by s.shopClassId" )
List<GroupShop> groupShop();
|
service:
1
2
|
//分组查询
List<GroupShop> groupShop();
|
Impl:
1
2
3
4
|
@Override
public List<GroupShop> groupShop() {
return shopRepository.groupShop();
}
|
controller:
1
2
3
4
|
@GetMapping ( "/groupShop" )
public String groupShop(){
return JSON.toJSONString(shopService.groupShop());
}
|
七、与mybatis对比
- jpa是对象与对象之间的映射,而mybatis是对象和结果集的映射
- jpa移植性比较好,不用关心用什么数据库,因为mybatis*写sql语句,所以当项目移植的时候还需要改sql。
- 修改字段时JPA更简单,mybatis需要修改一堆的xml,mapper等文件很麻烦。
- mybatis自定义sql,比较灵活,也可以写复杂的sql,JPA只适合简单的单表sql
总结
mybatis和JPA各有优势,如果sql简单,则jpa使用效率更高,如果sql较复杂,需要自定义,则使用mybatis更加顺手。
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/CSDN_SHY/article/details/117923206