Spring Boot(五):Spring Boot Jpa 的使用
这篇文章将全面的介绍 Spring Boot Jpa 常见用法以及注意事项。
文章目录
Spring Boot Jpa 介绍
首先了解 Jpa 是什么?
Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术,结束现在 Hibernate,TopLink,JDO 等 ORM 框架各自为营的局面。
值得注意的是,Jpa是在充分吸收了现有 Hibernate,TopLink,JDO 等 ORM 框架的基础上发展而来的,具有易于使用,伸缩性强等优点。从目前的开发社区的反应上看,Jpa 受到了极大的支持和赞扬,其中就包括了 Spring 与 EJB3. 0的开发团队。
注意:Jpa 是一套规范,不是一套产品,那么像 Hibernate,TopLink,JDO 他们是一套产品,如果说这些产品实现了这个 Jpa 规范,那么我们就可以叫他们为 Jpa 的实现产品。
Spring Boot Jpa
Spring Boot Jpa 是 Spring 基于 ORM 框架、Jpa 规范的基础上封装的一套 Jpa 应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data Jpa 可以极大提高开发效率!
Spring Boot Jpa 让我们解脱了 DAO 层的操作,基本上所有 CRUD 都可以依赖于它来实现
Spring Boot Jpa的初体验
1.导入依赖
<!-- 使用jpa规范mysql数据库-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
2.准备数据库环境
为这个项目,我们专门新建一个 springboot的数据库,并且给 springboot 用户授权
create database springboot_jpa;
grant all privileges on springboot_jpa.* to 'springboot'@'%' identified by 'springboot';
flush privileges; //刷新权限
3.项目配置
在配置文件中,完成对数据库的配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:localhost:3306/springboot
password: springboot
username: springboot
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true # 配置在日志中打印出执行的 SQL 语句信息
hibernate:
ddl-auto: update
#hibernate.ddl-auto属性介绍:
#可以看到ddl-auto里面有一些属性。他们分别是:update,create,create-drop,validate
#update:每次运行改程序,没有表格会新建表格,表格里面的数据不会清空,只会进行更新
#create:每次运行改程序,没有表格会新建表格,表格里面的数据会清空
#create-drop:每次运行后,会清空表数据
#validate:运行程序会校验数据与数据库的字段类型是否相同,不一样会报错的。
#我们一般常用的话,为了保存数据,会经常使用update
4.创建实体类
@Entity //必须填写,因为他表示该实体类对应一个表
@Table(name="user")//设置表名,要在idea加入数据库 DataBase
public class User {
@Id
private long id;//@Id 注解声明了实体唯一标识对应的属性。
@Column(length =32)
//@Column(length = 32) 用来声明实体属性的表字段的定义。默认的实体每个属性都对应了表的一个字段。字段的名称默认和属性名称保持一致(并不一定相等)。字段的类型根据实体属性类型自动推断。这里主要是声明了字符字段的长度。如果不这么声明,则系统会采用 255 作为该字段的长
private String name;
@Column(length =32)
private String pwd;
public User() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
5.实现一个持久层服务
在 Spring Data JPA 的世界里,实现一个持久层的服务是一个非常简单的事情。以上面的 User实体对象为例,我们要实现一个增加、删除、修改、查询功能的持久层服务,那么我只需要声明一个接口,这个接口继承
org.springframework.data.repository.Repository<T, ID> 接口或者他的子接口就行。这里为了功能的完备,我们继承了 org.springframework.data.jpa.repository.JpaRepository<T, ID> 接口。其中 T是数据库实体类,ID 是数据库实体类的主键的类型。然后再简单的在这个接口上增加一个 @Repository 注解就结束了。
@Repository
public interface UserDao extends JpaRepository<User, Long> {
}
6.测试方法
@RunWith(SpringRunner.class) //用容器方式运行
@SpringBootTest //@SpringBootTest替代了spring-test中的@ContextConfiguration注解,目的是加载ApplicationContext,启动spring容器。
public class JpaTest {
@Autowired
private UserDao userDao;
@Before
public void before(){
User user=new User();
user.setId(111111L);
user.setName("应该是阿杰");
user.setPwd("123456");
userDao.save(user);
}
@Test
public void test(){
List<User> all = userDao.findAll();
System.out.println(all.get(0));
}
@After
public void after(){
boolean b = userDao.existsById(111111L);
System.out.println(b);
}
}
自定义简单查询
经过刚才的体验我们可以看到,查询功能是不尽人意的,很多我们想要的查询功能还没有。不过放心。JPA 有非常方便和优雅的方式来解决
如果想要根据实体的某个属性来进行查询我们可以在 UserDao 接口中进行接口声明。
User findByAccount(String name);
也使用一些加一些关键字And
、 Or
User findByNameOrId(String username, long id);
修改、删除、统计也是类似语法
Long deleteById(Long id);
Long countByUserName(String userName)
基本上 SQL 体系中的关键词都可以使用,例如:LIKE
、 IgnoreCase
、 OrderBy
。
List<User> findByEmailLike(String email);
User findByUserNameIgnoreCase(String userName);
List<User> findByUserNameOrderByEmailDesc(String email);
具体的关键字,使用方法和生产成SQL如下表所示
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
TRUE | findByActiveTrue() | … where x.active = true |
FALSE | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
复杂查询
在实际的开发中我们需要用到分页、删选、连表等查询的时候就需要特殊的方法或者自定义 SQL
分页查询
分页查询在实际使用中非常普遍了,Spring Boot Jpa 已经帮我们实现了分页的功能,在查询的方法中,需要传入参数Pageable
,当查询中有多个参数的时候Pageable
建议做为最后一个参数传入.
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
Pageable
是 Spring 封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则
@Test
public void test(){
Sort sort = Sort.by(Sort.Direction.DESC, "id");
Pageable pageable=PageRequest.of(1,2,sort);
Page<User> all = userDao.findAll(pageable);
}
//springboot2.2.1(含)以上的版本Sort已经不能再实例化了,构造方法已经是私有的了!可以改用Sort.by获得Sort对象。PageRequest也只能用of
限制查询
有时候我们只需要查询前N个元素,或者支取前一个实体。
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
自定义SQL查询
其实 Spring Data 觉大部分的 SQL 都可以根据方法名定义的方式来实现,但是由于某些原因我们想使用自定义的 SQL 来查询,Spring Data 也是完美支持的;在 SQL 的查询方法上面使用@Query
注解,如涉及到删除和修改在需要加上@Modifying
.也可以根据需要添加 @Transactional
对事物的支持,查询超时的设置等。
@Modifying
@Query("update User u set u.userName = ?1 where u.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);
多表查询(待补充)
多表查询 Spring Boot Jpa 中有两种实现方式,第一种是利用 Hibernate 的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里主要第二种方式。
多数据源的支持
同源数据库的多源支持
日常项目中因为使用的分布式开发模式,不同的服务有不同的数据源,常常需要在一个项目中使用多个数据源,因此需要配置 Spring Boot Jpa 对多数据源的使用,一般分一下为三步:
- 1 配置多数据源
- 2 不同源的实体类放入不同包路径
- 3 声明不同的包路径下使用不同的数据源、事务支持
异构数据库多源支持
比如我们的项目中,即需要对 mysql 的支持,也需要对 Mongodb 的查询等。
实体类声明@Entity
关系型数据库支持类型、声明@Document
为 Mongodb 支持类型,不同的数据源使用不同的实体就可以了
interface PersonRepository extends Repository<Person, Long> {
…
}
@Entity
public class Person {
…
}
interface UserRepository extends Repository<User, Long> {
…
}
@Document
public class User {
…
}
但是,如果 User 用户既使用 Mysql 也使用 Mongodb 呢,也可以做混合使用
interface JpaPersonRepository extends Repository<Person, Long> {
…
}
interface MongoDBPersonRepository extends Repository<Person, Long> {
…
}
@Entity
@Document
public class Person {
…
}
也可以通过对不同的包路径进行声明,比如 A 包路径下使用 mysql,B 包路径下使用 MongoDB
@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.neo.repositories.mongo")
interface Configuration { }
其它
使用枚举
使用枚举的时候,我们希望数据库中存储的是枚举对应的 String 类型,而不是枚举的索引值,需要在属性上面添加@Enumerated(EnumType.STRING)
注解
@Enumerated(EnumType.STRING)
@Column(nullable = true)
private UserType type;
不需要和数据库映射的属性
正常情况下我们在实体类上加入注解@Entity
,就会让实体类和表相关连如果其中某个属性我们不需要和数据库来关联只是在展示的时候做计算,只需要加上@Transient
属性既可。
@Transient
private String userName;
参考文章
https://blog.csdn.net/ityouknow/article/details/80490926?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160462735719724835832495%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=160462735719724835832495&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v1~rank_blog_v1-26-80490926.pc_v1_rank_blog_v1&utm_term=springboot&spm=1018.2118.3001.4450