SpringBoot - Spring Data JPA集成
Spring Boot集成Spring Data JPA实操步骤
一、简介
Spring Data JPA是 Spring 基于 ORM 框架、JPA 规范封装的一套 JPA 应用框架, 可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展! (官方文档、GitHub地址)
二、使用
1.导入包
在pom.xml文件中引入下述依赖(版本可自主选择)。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
2.基本配置
在resources文件夹下创建application.yml
文件并进行下述配置。
注
:application.yml与application.properties作用相同,但前者更加简洁、层次分明、更便于阅读,因此使用application.yml。
spring:
datasource:
url: jdbc:mysql://localhost:3306/dataBaseName?serverTimezone=Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: anchor#123
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate:
ddl-auto: update
show-sql: true
其中
:
1)”url: jdbc:mysql://localhost:3306
/dataBaseName
?serverTimezone=Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true”
“localhost:3306”为数据库IP及端口(端口一般为3306),”dataBaseName”为要连接的数据库名称,其余不做修改;
2)”username“和”password“为数据库账号和密码(若忘记可重置);
3)”driver-class-name“:说明;
4)”database-platform“:数据库引擎(介绍);
5)”ddl-auto“:属性介绍,一般使用update;
6)”show-sql“:是否在日志中打印真实操作的sql语句;
3.项目结构
四类约定文件
:
1)model:存放数据库中表对应的Java类;
2)repository:每个Repository接口负责对一张表进行CURD,是Java应用操作数据库的工具;
3)service:业务操作,普遍一个Service类中封装一个Repository的相关操作,对Repository查到的数据进行业务处理;
4)controller:与外界(请求)交互的门户,接收外界请求并调用service层的方法处理该请求;
创建类时请遵循上述约定将对应的类放在对应的package下!
三、细解
(users表) (detail表)
1.Model
默认情况下,创建实体类时注意类名要与数据库中表名相同,类中各属性名与表中字段名相同。(符合驼峰也可匹配)
@Entity /*用于标注该类对应数据库的表*/ @Entity
@Table(name = "users") /*用于映射表名和实体类名*/ public class Detail {
public class User { @Id
@Id /*用于标注该字段为User类中的主键*/ @GeneratedValue
@GeneratedValue /*用于标注主键的生成策略*/ private long id;
private long id; private String position;
private String name; private String phoneNumber; //驼峰
private int age; @Column(name = "education")
private String description; private String edu;
} /*省略getter、setter方法*/ } //省略getter、setter方法
1)@Entity
用于告诉spring boot该类对应数据库的一张表。
2)@Table
当实体类类名与其映射的数据库表名不相同时需要使用@Table注解指定实体类所对应的数据库表。
用法:@Table(name = “tableName”)
3)@Column
当类属性名与数据库字段名不相同时需要使用@Column注解指定属性所对应的字段。
用法:@Column(name = “columnName”)
4)@GeneratedValue
其意义主要是为一个实体生成一个唯一标识的主键,通过strategy属性指定生成方式,共有四种策略。
用法:@GeneratedValue(strategy=GenerationType.AUTO)
①IDENTITY:采用数据库ID自增长的方式来自增主键字段,Oracle 不支持这种方式;
②AUTO:JPA自动选择合适的策略,是默认选项(不指定strategy时默认使用AUTO);
③SEQUENCE:通过数据库的序列产生主键,通过@SequenceGenerator注解指定序列名,MySql不支持这种方式;
④TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植;
2.Repository
Repository作为对数据库进行实际操作的类(接口),JPA已经提供了大量常用的CURD方法,封装在JpaRepository、PagingAndSortingRepository、
CrudRepository等接口中,一般情况继承JpaRepository<T,ID>即可使用所有现有方法。
JpaRepository<T,ID>:其中T为数据库表对应的Java类,ID为Java类中主键的类型。
@Repository //用于标注数据库访问组件(也可不写,Spring Boot也可正常工作)
public interface UsersRepository extends JpaRepository<User,Long> { //用于操作数据库"users"表
}
默认方法开箱即可使用,约定方法只需根据约定起好方法名,自定义方法需自己编写HQL/SQL语句。
1)默认方法
以下方法为三个接口中自带,可直接使用。
a.新增和更新
新增和更新单条记录都使用S save(S var1)
方法。多条记录时使用重载方法saveAll(Iterable<S> var1)
,注意两个方法返回值不同。
①当var1变量的主键为空时,执行新增操作;
②当主键不为空、但数据库中主键对应的数据不存在时,执行新增操作;
③当主键不为空、且数据库中主键对应的数据存在时,执行更新操作;
当使用了@GeneratedValue注解后,即使id赋了具体的值(例id=999),save时还是会使用自增长的方式,而不会在数据库中将id的值存为999。
b.排序和分页
重载方法findAll()可以传入Sort或实现Pageable的对象来进行分页或排序。
①排序
spring-data-commons-2.2.0.RELEASE.jar中不再提供Sort的public构造方法,改为使用Sort.by()来创建Sort实例。
Sort.Direction.ASC是升序,对应Sort.Direction.DESC是降序,”id”为Java类中的属性名,注意不是数据库表中的字段名。
public List<User> singleSort(){ //单属性排序
return usersRepository.findAll(Sort.by(Sort.Direction.ASC, "id"));
}
public List<User> multiSort() { //多属性排序
Sort.Order idOrder = Sort.Order.asc("id");
Sort.Order ageOrder = Sort.Order.desc("age");
return usersRepository.findAll(Sort.by(idOrder, ageOrder)); //先按id升序再按age降序进行排序
}
②分页
PageRequest是实现了Pageable接口的类,使用PageRequest.of()来创建PageRequest实例。
PageRequest.of()有三个重载方法,可按需选用。
public List<User> page(int pageNum, int pageSize){ //pageNum表示第几页,pageSize表示每页有几条数据
Sort sort = Sort.by(Sort.Direction.ASC, "id"); //根据id进行排序
PageRequest pageRequest = PageRequest.of(pageNum, pageSize, sort);
Page<User> userPage = usersRepository.findAll(pageRequest);
return userPage.getContent(); //getContent()方法可获取查询结果,
} //实际情况下至少还需返回查询总数:userPage.getTotalElements()
c.按例查询
这里的”例”是Example,给repository传入一个Example对象,JPA会根据这个例子去查询符合条件的数据。
个人感觉按例查询比约定方法使用更为繁杂,不建议使用。想了解可自行google。
2)约定方法
①Spring Data JPA框架可根据约定好的方法名自动生成相应的SQL语句,主要的语法是findXXBy后面跟属性名称。
②由于Create和Update一般是操作一组具体数据,删除是根据主键,默认提供的方法已足够使用,因此约定方法基本用来Read。
③使用方法
,根据表格中关键字生成相应的查询方法。一般使用findXX,但也可用readXX、queryXX、getXX。
@Repository
public interface UsersRepository extends JpaRepository<User,Long> {
User findByNameAndAge(String name, int age);
List<User> readByAgeLessThanEqual(int age);
List<User> queryByNameStartingWith(String word);
List<User> getByDescriptionIsNotNull();
@Transactional
@Modifying
int deleteByName(String name);
List<User> findByAge(Sort sort); //约定方法排序。方法名findByAge后加"BySort"会报错
Page<User> findByAge(Pageable pageable); //约定方法分页。方法名findByAge后加"ByPageable"会报错
}
3)自定义方法
当上述两类方法都无法满足对数据库的操作需求时,可以使用@Query注解来自定义操作方法。(自定义查询语句)
@Query可用于新增、更新、查询、删除操作。但不建议用于新增,默认方法中新增更加便捷。
当方法用于新增、更新、删除时,需再加注解@Modifying和@Transactional。
a.使用HQL
普通@Query是使用hql进行操作。hql语句中的操作对象是Java类名和类的属性名,而不是数据库表的表名和字段名。
public interface UsersRepository extends JpaRepository<User,Long> {
@Query("SELECT u from User u where u.name = ?1 and u.age = ?2") //"?1"代表第一个参数,"?2"代表第二个参数,以此类推。
User findByNameAndAge(String name, int age);
@Query("select u from User u where u.name = :myName and u.age = :myAge") //":参数名"的方式指定方法中的参数
User findByNameAndAge(@Param("myName") String name, @Param("myAge") int age); //使用@Param注解注入参数
}
个人认为使用”?序号”的方式注入参数更加简洁。
b.使用SQL
在@Query注解中设置”nativeQuery = true”来使用原生sql。
public interface UsersRepository extends JpaRepository<User,Long> {
@Query(value = "SELECT * from users where name= ?1", nativeQuery = true) //value中为普通的sql语句
User findByName(String name);
} //也可使用@Param注解来注入参数,此处未写出
c.排序和分页
@Query("select u from User u where u.age = ?1")
List<User> findByAge(int age, Sort sort); //自定义方法的排序
@Query("select u from User u where u.age = ?1")
Page<User> findByAge(int age, Pageable pageable); //自定义方法的分页
d.根据值长度排序
public List<User> sortByLength(int age) {
JpaSort sort = JpaSort.unsafe(Sort.Direction.ASC, "LENGTH(name)"); //LENGTH(属性名)。必须使用JpaSort.unsafe,使用Sort.by()报错
return usersRepository.findByAge(age, sort);
}
注意事项
:
①当使用@Query注解时,约定方法会失效。如下findByName方法实际查询条件是@Query中的age,而非方法名中的ByName。
即@Query注解优先级高于约定。
@Query(value = "SELECT * from users where age= ?1", nativeQuery = true)
User findByName(int age);
②若进行新增、更新、删除操作不添加@Modifying和@Transactional,会分别抛出如下异常。
java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
javax.persistence.TransactionRequiredException: Executing an update/delete query
③更新、删除操作方法的返回值只能是void或int/Integer,否则会抛出如下异常。
java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type!
④只有自定义方法才可以根据值长度进行排序,默认方法和约定方法使用”LENGTH(属性名)”排序会报错。
org.springframework.data.mapping.PropertyReferenceException: No property LENGTH(description) found for type User!
⑤三种关于HQL的错误
写法
@Query("SELECT u from users u where u.name = ?1 and u.age = ?2") //无法识别users。hql操作实体类,而不是数据库表(应是User类,而不是users表)
User findByNameAndAge(String name, int age);
@Query("SELECT * from User where name = ?1 and age = ?2") //hql不支持"*"写法
User findByNameAndAge(String name, int age);
@Query("SELECT User from User u where u.name = ?1 and u.age = ?2") //启动时报一个空指针错误(It confuses me)
User findByNameAndAge(String name, int age);
3.Service
在Service层注入Repository后即可使用其中定义的各类方法。
文末代码仓库中有使用示例。
@Component
public class UsersService {
@Resource //推荐使用@Resource,不推荐@Autowired(具体原因自行google)
private UsersRepository usersRepository;
/**
* 使用默认方法 - 新增或更新一条数据
*/
public User saveUser(User user) {
return usersRepository.save(user);
}
//...
}
4.Controller
常规使用
@RestController
@RequestMapping("/user")
public class UsersController {
@Resource
private UsersService usersService;
@RequestMapping("/all")
public List<User> getAllUsers() {
return usersService.getAllByDefault();
}
//类似调用UsersService中方法即可
//...
}
本文中使用的所有代码:https://github.com/memorate/SpringBootJPA,此工程中所有代码皆可正常运行。