JPA 即 Java Persistence API,是一个基于O/R 映射的标准规范。Hibernate 是该规范的实现,Hibernate 是非常流行的对象关系映射框架(ORM
),是SSH
组合开发框架重要组件。
Spring Data JPA 是 Spring Data 的一个子项目,它提供了基于 JPA 的 Repository 减少了大量的数据访问操作的代码。JpaRepository 接口提供了通用的数据访问方法。
始终建议看官方文档: Spring Boot 2.0.3 > Use Spring Data Repositories Spring Data JPA - Reference Documentation
JDBC自动配置 Spring Boot 自动配置依赖于 spring-boot-autoconfigure-2.0.1.RELEASE.jar 包,默认支持的自动配置都在org.springframework.boot.autoconfigure路径下。
自动配置 spring-boot-starter-data-jpa 依赖于spring-boot-starter-jdbc, Spring Boot 为 JDBC 做了些自动配置,并自动开启了注解事务的支持。JDBC 自动配置源码在 org.springframework.boot.autoconfigure.jdbc 路径下。
自动配置文件:DataSourceAutoConfiguration.class 自动配置类会被注册为Bean
并执行里面的配置。
配置属性文件:DataSourceProperties.class ,可以通过spring.datasource
为前辍的属性自动配置数据源。
自动开启注解事务文件,DataSourceTransactionManagerAutoConfiguration.class 自动配置事务管理,并配置了一个JdbcTemplate
。
表结构初始化 Spring Boot 提供了初始化数据的功能:放置在类路径(/src/main/resources)下的schema.sql
文件会自动用来初始化表结构;放置在类路径下的data.sql
文件会自动用来填充表数据。
实际项目中几乎不会使用,也不建议使用;实际开发是先根据业务逻辑设计好数据库,再定实体类,再进行业务逻辑代码开发;数据库设计先行能更好更深入的理解业务逻辑和关联关系。
JPA自动配置分析
Spring Boot 对 JPA 的自动配置在 org.springframework.boot.autoconfigure.orm.jpa 下,从该包下的HibernateJpaAutoConfiguration.class 可以看出,Spring Boot 默认支持的 JPA 实现是Hibernate, HibernateJpaAutoConfiguration 依赖于 JDBC
的自动配置类DataSourceAutoConfiguration。
从 JpaProperties.class 属性配置类可以看到,配置 JPA 可以使用 spring.jpa 为前缀的属性在 application.properties 中配置。
JpaBaseConfiguration.class 类配置了 transactionManager,jpaVendorAdapter,entityManagerFactory 等 Bean。还提供了getPackagesToScan() 方法用于自动扫描注解 @Entity 的实体类。
Spring Data JPA自动配置
对 Spring Data JPA 的自动配置放在 org.springframework.boot.autoconfigure.data.jpa 包下。
从 JpaRepositoriesAutoConfiguration.class 配置类中可以看到该自动配置依赖于 HibernateJpaAutoConfiguration 配置。该配置类还引入了JpaRepositoriesAutoConfigureRegistrar.class 注册类,该注册启用了 EnableJpaRepositories,所以在 Spring Boot 项目中配置类就无须声明@EnableJpaRepositories。
Spring Boot JPA配置 在项目中添加spring-boot-starter-data-jpa
依赖,会自动添加jdbc
依赖,然后只需定义DataSource
、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的 Bean 即可,无须额外的配置。
JpaRepositories类图
Spring Boot JPA使用示例 项目示例
新建Spring Boot 项目,引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-jpa</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency >
在application.properties
配置数据源。
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 server.port =80 spring.profiles.active =dev logging.config =classpath:log4j2.xml spring.datasource.driverClassName = net.sf.log4jdbc.DriverSpy spring.datasource.url =jdbc:log4jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8 spring.datasource.username =admin spring.datasource.password =123456 spring.jpa.show-sql =true spring.jpa.database-platform =org.hibernate.dialect.MySQL5Dialect spring.jackson.serialization.indent-output =true
定义实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Entity public class Actor { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long actorId; private String firstName; private String lastName; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date lastUpdate; }
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 import com.springboot.jpa.entity.Actor;import com.springboot.jpa.service.ActorService;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Sort;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Date;import java.util.List;@RestController @RequestMapping(value = "/actor") public class ActorController { private static final Logger logger = LogManager.getLogger(ActorController.class); @Autowired private ActorService actorService; @RequestMapping(value = "/addActor") public Actor addActor (Actor actor) { actor.setLastUpdate(new Date ()); return actorService.addActor(actor); } @RequestMapping(value = "/deleteByActorId") public void deleteByActorId (Long actorId) { actorService.deleteByActorId(actorId); } @RequestMapping(value = "/updateFirstName") public int updateFirstName (Long actorId, String firstName) { return actorService.updateFirstName(actorId, firstName); } @RequestMapping("/queryAll") public List<Actor> queryAll () { return actorService.queryAll(); } @RequestMapping("/queryByActorId") public Actor queryByActorId (Long actorId) { return actorService.queryByActorId(actorId); } @RequestMapping("/queryByFirstName") public List<Actor> queryByFirstName (String firstName) { return actorService.queryByFirstName(firstName); } @RequestMapping("/queryByLastName") public List<Actor> queryByLastName (String lastName) { return actorService.queryByLastName(lastName); } @RequestMapping("/queryByFirstNameAndLastName") public List<Actor> queryByFirstNameAndLastName (String firstName, String lastName) { return actorService.queryByFirstNameAndLastName(firstName,lastName); } @RequestMapping("/queryByFirstNameWithSortDesc") public List<Actor> queryByFirstNameWithSortDesc (String firstName) { Sort sort = new Sort (Sort.Direction.ASC, "firstName" ); return actorService.queryByFirstNameWithSortDesc(sort); } @RequestMapping("/queryByPage") public Page<Actor> queryByPage (int page, int size) { PageRequest pageRequest = PageRequest.of(page, size); return actorService.queryByPage(pageRequest); } @RequestMapping("/count") public Long countActor () { return actorService.countActor(); } @RequestMapping("/countBy") public Long countBy (Actor actor) { return actorService.countBy(actor); } }
业务接口
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 import com.springboot.jpa.entity.Actor;import org.springframework.data.domain.Page;import org.springframework.data.domain.PageRequest;import org.springframework.data.domain.Sort;import java.util.List;public interface ActorService { Actor addActor (Actor actor) ; void deleteByActorId (Long actorId) ; List<Actor> queryAll () ; Actor queryByActorId (Long actorId) ; Long countActor () ; Long countBy (Actor actor) ; List<Actor> queryByFirstName (String firstName) ; int updateFirstName (Long actorId, String firstName) ; List<Actor> queryByLastName (String lastName) ; List<Actor> queryByFirstNameAndLastName (String firstName, String lastName) ; List<Actor> queryByFirstNameWithSortDesc (Sort sort) ; Page<Actor> queryByPage (PageRequest pageRequest) ; }
业务接口实现类代码
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 import com.springboot.jpa.entity.Actor;import com.springboot.jpa.repository.ActorRepository;import com.springboot.jpa.service.ActorService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.*;import org.springframework.stereotype.Service;import java.util.List;@Service public class ActorServiceImpl implements ActorService { @Autowired private ActorRepository actorRepository; @Override public Actor addActor (Actor actor) { return actorRepository.save(actor); } @Override public void deleteByActorId (Long actorId) { actorRepository.deleteById(actorId); } @Override public int updateFirstName (Long actorId, String firstName) { return actorRepository.updateFirstName(actorId, firstName); } @Override public List<Actor> queryAll () { return actorRepository.findAll(); } @Override public Actor queryByActorId (Long actorId) { return actorRepository.findById(actorId).get(); } @Override public List<Actor> queryByFirstName (String firstName) { Example<Actor> example = Example.of(new Actor ().setFirstName(firstName)); return actorRepository.findAll(example); } @Override public List<Actor> queryByFirstNameAndLastName (String firstName, String lastName) { Actor actor = new Actor ().setFirstName(firstName).setLastName(lastName); Example<Actor> example = new Example <Actor>() { @Override public Actor getProbe () { return actor; } @Override public ExampleMatcher getMatcher () { return ExampleMatcher.matchingAny(); } }; return actorRepository.findAll(Example.of(actor, ExampleMatcher.matchingAll())); } @Override public List<Actor> queryByFirstNameWithSortDesc (Sort sort) { return actorRepository.findAll(sort); } @Override public Page<Actor> queryByPage (PageRequest pageRequest) { return actorRepository.findAll(pageRequest); } @Override public List<Actor> queryByLastName (String lastName) { return actorRepository.queryByLastName(lastName); } @Override public Long countActor () { return actorRepository.count(); } @Override public Long countBy (Actor actor) { return actorRepository.count(Example.of(actor)); } }
定义数据访问接口 继承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 35 36 37 import com.springboot.jpa.entity.Actor;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.Modifying;import org.springframework.data.jpa.repository.Query;import org.springframework.data.repository.query.Param;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Transactional;import java.util.List;@Repository public interface ActorRepository extends JpaRepository <Actor, Long> { @Modifying @Transactional(timeout = 1000) @Query("update Actor set firstName = ?2 where actorId = ?1") int updateFirstName (Long actorId, String firstName) ; @Query(value = "select a from Actor a where a.lastName = :lastName") List<Actor> queryByLastName (@Param("lastName") String lastName) ; }
JPA常用注解
**@Entity:**表示这是一个与数据库映射的实体类(作用在类上)。
**@Table:**表示该实体类映射到的表名(作用在类上)。
**@Id:**表示该属性映射为数据库表的主键(作用在属性上)。
**@GeneratedValue:**指示主键生成策略(作用在属性上)。
GenerationType.TABLE
GenerationType.SEQUENCE
GenerationType.IDENTITY
GenerationType.AUTO
**@Column:**表示该属性映射到表的字段名(作用在属性上)。
@Temporal: Date类型属性映射到表字段的具体日期时间类型(作用在属性上),可设置如下值:
TemporalType.DATE
TemporalType.TIME
TemporalType.TIMESTAMP
**@Inheritance:**指定继承(父子)关系的实体类映射到表的策略(作用在类上)。
InheritanceType.SINGLE_TABLE
InheritanceType.TABLE_PER_CLASS
InheritanceType.JOINED
示例代码>github 文章源码(springboot-data-jpa) -> GitHub
【参考】@Query注解的用法(Spring Data JPA) @Query注解及@Modifying注解