Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。Spring Data JPA旨在通过减少实际需要的数量来显着提高数据访问层的实现。
在Spring环境中需要配置大量的XML配置,但是SpringBoot基本上帮助我们配置好了,我们只需要简单地配置一下DataSource和几项jpa额外的配置就完成了整个配置持久化层的工作。EntityManagerFactory 那些都不用配置了。
JPA不是一种框架,而只是一种ORM规范,而这种规范具体由其他厂商来实现,其中最熟悉的莫过于Hibernate了。
网络上对JPA的褒贬不一,特别是用JPA进行多表操作的时候,确实是比较繁琐。当然任何技术有好的方面也有坏的方面,本章重点不在于此。
<!-- jpa依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///springboot
spring.datasource.username=root
spring.datasource.password=admin
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
在Spring Boot自动配置的时候,一旦引入spring-boot-starter-data-jpa
,就会完成JpaRepositoriesAutoConfiguration
的自动配置。
@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {}
spring-data-jpa底层使用的是Hibernate作为实现,所以jpa的自动配置操作在Hibernate的自动配置之后。
@Configuration
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class })
@Conditional(HibernateEntityManagerCondition.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
//other...
public HibernateJpaAutoConfiguration(DataSource dataSource,
JpaProperties jpaProperties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
//调用父类的构造方法
super(dataSource, jpaProperties, jtaTransactionManager,
transactionManagerCustomizers);
}
@Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Override
protected Map<String, Object> getVendorProperties() {
Map<String, Object> vendorProperties = new LinkedHashMap<String, Object>();
vendorProperties.putAll(getProperties().getHibernateProperties(getDataSource()));
return vendorProperties;
}
//other...
}
在使用Spring和JPA集成的时候,会配置一个jpaVendorAdapter属性,一般使用HibernateJpaVendorAdapter作为JPA持久化实现厂商类。如下是spring和jpa集成时的部分配置:
<property name="jpaVendorAdapter">
<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- 该bean的属性配置 -->
</bean>
</property>
在JpaBaseConfiguration类中有个createJpaVendorAdapter()抽象方法,而在HibernateJpaAutoConfiguration类中进行了重载,创建使用HibernateJpaVendorAdapter作为JPA底层持久化实现厂商。
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
private final DataSource dataSource;
private final JpaProperties properties;
private final JtaTransactionManager jtaTransactionManager;
private final TransactionManagerCustomizers transactionManagerCustomizers;
private ConfigurableListableBeanFactory beanFactory;
protected JpaBaseConfiguration(DataSource dataSource, JpaProperties properties,
ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
this.dataSource = dataSource;
this.properties = properties;
this.jtaTransactionManager = jtaTransactionManager.getIfAvailable();
this.transactionManagerCustomizers = transactionManagerCustomizers
.getIfAvailable();
}
//创建了TransactionManager的Bean
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
//创建了jpaVendorAdapter适配器,并填充相应属性,最后返回
@Bean
@ConditionalOnMissingBean
public JpaVendorAdapter jpaVendorAdapter() {
//通过HibernateJpaAutoConfiguration子类创建HibernateJpaVendorAdapter适配器
AbstractJpaVendorAdapter adapter = createJpaVendorAdapter();
adapter.setShowSql(this.properties.isShowSql());
adapter.setDatabase(this.properties.determineDatabase(this.dataSource));
adapter.setDatabasePlatform(this.properties.getDatabasePlatform());
adapter.setGenerateDdl(this.properties.isGenerateDdl());
return adapter;
}
//通过jpaVendorAdapter与其他配置信息创建Builder构建器
@Bean
@ConditionalOnMissingBean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder(
JpaVendorAdapter jpaVendorAdapter,
ObjectProvider<PersistenceUnitManager> persistenceUnitManager) {
EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(
jpaVendorAdapter, this.properties.getProperties(),
persistenceUnitManager.getIfAvailable());
builder.setCallback(getVendorCallback());
return builder;
}
//创建LocalContainerEntityManagerFactoryBean的Bean,用于JPA的容器管理EntityManagerFactory
@Bean
@Primary
@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class,
EntityManagerFactory.class })
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder factoryBuilder) {
Map<String, Object> vendorProperties = getVendorProperties();
customizeVendorProperties(vendorProperties);
return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan())
.properties(vendorProperties).jta(isJta()).build();
}
//other...
}
以上配置信息类似一个xml配置:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置jpa的EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 配置jpa生产商的适配器 -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<!-- 配置jpa的基本属性 -->
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!-- 配置SpringData -->
<jpa:repositories base-package="xxxxx包名" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"></jpa:repositories>
Repository Jpa基层接口
public interface Repository<T, ID extends Serializable> {}
CrudRepository 专门用于crud的接口
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); <S extends T> Iterable<S> save(Iterable<S> entities); T findOne(ID id); boolean exists(ID id); Iterable<T> findAll(); Iterable<T> findAll(Iterable<ID> ids); long count(); void delete(ID id); void delete(T entity); void delete(Iterable<? extends T> entities); void deleteAll(); }PagingAndSortingRepository 带有简单的分页和排序的接口
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }//排序类 public class Sort implements Iterable<org.springframework.data.domain.Sort.Order>, Serializable { //默认以升序排序 public static final Direction DEFAULT_DIRECTION = Direction.ASC; private final List<Order> orders; public Sort(Order... orders) { this(Arrays.asList(orders)); } public Sort(List<Order> orders) { if (null == orders || orders.isEmpty()) { throw new IllegalArgumentException("You have to provide at least one sort property to sort by!"); } this.orders = orders; } public Sort(String... properties) { this(DEFAULT_DIRECTION, properties); } public Sort(Direction direction, String... properties) { this(direction, properties == null ? new ArrayList<String>() : Arrays.asList(properties)); } public Sort(Direction direction, List<String> properties) { if (properties == null || properties.isEmpty()) { throw new IllegalArgumentException("You have to provide at least one property to sort by!"); } this.orders = new ArrayList<Order>(properties.size()); for (String property : properties) { this.orders.add(new Order(direction, property)); } } public Sort and(Sort sort) { if (sort == null) { return this; } ArrayList<Order> these = new ArrayList<Order>(this.orders); for (Order order : sort) { these.add(order); } return new Sort(these); } public Order getOrderFor(String property) { for (Order order : this) { if (order.getProperty().equals(property)) { return order; } } return null; } //other... //内部定义了一个Direction的枚举类 public static enum Direction { ASC, DESC; //other code... } //内部定义了Order类,用于字段排序 public static class Order implements Serializable { private static final boolean DEFAULT_IGNORE_CASE = false; private final Direction direction; private final String property; private final boolean ignoreCase; private final NullHandling nullHandling; public Order(Direction direction, String property) { this(direction, property, DEFAULT_IGNORE_CASE, null); } public Order(Direction direction, String property, NullHandling nullHandlingHint) { this(direction, property, DEFAULT_IGNORE_CASE, nullHandlingHint); } public Order(String property) { this(DEFAULT_DIRECTION, property); } private Order(Direction direction, String property, boolean ignoreCase, NullHandling nullHandling) { if (!StringUtils.hasText(property)) { throw new IllegalArgumentException("Property must not null or empty!"); } this.direction = direction == null ? DEFAULT_DIRECTION : direction; this.property = property; this.ignoreCase = ignoreCase; this.nullHandling = nullHandling == null ? NullHandling.NATIVE : nullHandling; } //other... } }//分页接口 public interface Pageable { int getPageNumber(); int getPageSize(); int getOffset(); Sort getSort(); Pageable next(); Pageable previousOrFirst(); Pageable first(); boolean hasPrevious(); }//分页抽象类 public abstract class AbstractPageRequest implements Pageable, Serializable { private final int page; private final int size; public AbstractPageRequest(int page, int size) { //other code... this.page = page; this.size = size; } public int getPageSize() { return size; } public int getPageNumber() { return page; } public int getOffset() { return page * size; } public boolean hasPrevious() { return page > 0; } public Pageable previousOrFirst() { return hasPrevious() ? previous() : first(); } public abstract Pageable next(); public abstract Pageable previous(); public abstract Pageable first(); //other code... }//分页实现类 public class PageRequest extends AbstractPageRequest { private final Sort sort; public PageRequest(int page, int size) { this(page, size, null); } public PageRequest(int page, int size, Direction direction, String... properties) { this(page, size, new Sort(direction, properties)); } public PageRequest(int page, int size, Sort sort) { super(page, size); this.sort = sort; } public Sort getSort() { return sort; } public Pageable next() { return new PageRequest(getPageNumber() + 1, getPageSize(), getSort()); } public PageRequest previous() { return getPageNumber() == 0 ? this : new PageRequest(getPageNumber() - 1, getPageSize(), getSort()); } public Pageable first() { return new PageRequest(0, getPageSize(), getSort()); } //other code... }JpaRepository 带有crud接口和分页、排序的接口
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort sort); List<T> findAll(Iterable<ID> ids); <S extends T> List<S> save(Iterable<S> entities); void flush(); <S extends T> S saveAndFlush(S entity); void deleteInBatch(Iterable<T> entities); void deleteAllInBatch(); T getOne(ID id); @Override <S extends T> List<S> findAll(Example<S> example); @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); }JpaSpecificationExecutor:用于复杂的条件查询并分页、排序功能
public interface JpaSpecificationExecutor<T> { T findOne(Specification<T> spec); List<T> findAll(Specification<T> spec); Page<T> findAll(Specification<T> spec, Pageable pageable); List<T> findAll(Specification<T> spec, Sort sort); long count(Specification<T> spec); }public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); }public class Specifications<T> implements Specification<T>, Serializable { private final Specification<T> spec; private Specifications(Specification<T> spec) { this.spec = spec; } public static <T> Specifications<T> where(Specification<T> spec) { return new Specifications<T>(spec); } public Specifications<T> and(Specification<T> other) { return new Specifications<T>(new ComposedSpecification<T>(spec, other, AND)); } public Specifications<T> or(Specification<T> other) { return new Specifications<T>(new ComposedSpecification<T>(spec, other, OR)); } public static <T> Specifications<T> not(Specification<T> spec) { return new Specifications<T>(new NegatedSpecification<T>(spec)); } public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { return spec == null ? null : spec.toPredicate(root, query, builder); } //other code... }
以上一大堆JPA内置方法,可以直接使用,而无需再声明定义了。当然如果想重载或者自定义的话,可以重新声明定义下。
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 |
findByFirstname ,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<Age> ages) |
… where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection<Age> 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) |
例如:
//使用自定义的@Query注解,实现sql语句
@Query("select count(*) from Person")
Long findAllCount();
@Query("from Person where id = ?1")
List<Person> selectWhereById(Integer id);
@Query("delete from Person where id = ?1")
@Modifying
void deleteById(Integer id);
@Query(value="select distinct id from person", nativeQuery = true)
List<Integer> getIds();
注意:
附上代码:
实体类Person
@Entity
@Table
public class Person implements Serializable{
@Id
@GeneratedValue
private Integer id;
private String name;
private int age;
//getter和setter
}
控制器类PersonController
@RestController
@RequestMapping("/person")
public class PersonController {
@Autowired
private IPersonService personService;
@RequestMapping("/find")
public Person findPersonByName(String name){
return personService.findPersonByName(name);
}
@PostMapping("/")
public String insertIntoPerson(Person person){
personService.insertIntoPerson(person);
return "success";
}
@GetMapping("/age")
public List<Person> getPersonAgeBetween(int min, int max){
return personService.getPersonAgeBetween(min, max);
}
@GetMapping("/page")
public Page<Person> getPersonsByPage(int pageNum, int pageSize){
return personService.getPersonsByPage(pageNum, pageSize);
}
@GetMapping("/sort")
public Page<Person> getPersonsBySortAge(){
return personService.getPersonsBySortAge();
}
@GetMapping("/count")
public Long getAllCount(){
return personService.findAllCount();
}
@PostMapping("/update")
public void updateOnePerson(Person person){
personService.updateOnePerson(person.getAge(), person.getName(), person.getId());
}
@GetMapping("/ids")
public List<Integer> getIds(){
return personService.getIds();
}
}
业务层接口IPersonService
public interface IPersonService {
Person findPersonByName(String name);
void insertIntoPerson(Person person);
List<Person> getPersonAgeBetween(int min, int max);
Page<Person> getPersonsByPage(int pageNum, int pageSize);
Page<Person> getPersonsBySortAge();
Long findAllCount();
void updateOnePerson(int age, String name, int id);
List<Integer> getIds();
}
业务层接口实现类PersonServiceImpl
@Service
public class PersonServiceImpl implements IPersonService {
@Autowired
private IPersonRepository personRepository;
@Override
public Person findPersonByName(String name) {
return personRepository.findPersonByName(name);
}
@Override
public void insertIntoPerson(Person person) {
personRepository.save(person);
}
@Override
public List<Person> getPersonAgeBetween(int min, int max) {
return personRepository.findPersonByAgeBetween(min, max);
}
@Override
public Page<Person> getPersonsByPage(int pageNum, int pageSize) {
Pageable pageable = new PageRequest(pageNum, pageSize);
return personRepository.findAll(pageable);
}
@Override
public Page<Person> getPersonsBySortAge() {
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC, "age"));
Pageable pageable = new PageRequest(0,5, sort);
return personRepository.findAll(pageable);
}
@Override
public Long findAllCount() {
return personRepository.findAllCount();
}
@Transactional
@Override
public void updateOnePerson(int age, String name, int id){
personRepository.updateOnePerson(age, name, id);
}
@Override
public List<Integer> getIds() {
return personRepository.getIds();
}
}
持久化接口IPersonRepository
public interface IPersonRepository extends JpaRepository<Person, Integer>{
Person findPersonByName(String name);
List<Person> findPersonByAgeBetween(int min, int max);
//使用自定义的@Query注解,实现sql语句
@Query("select count(*) from Person")
Long findAllCount();
//使用@Modifying注解表示该hql是个增删改的操作,需要事务管理
@Query("update Person set age = ?1, name = ?2 where id = ?3")
@Modifying
void updateOnePerson(int age, String name, int id);
//原生SQL
@Query(value="select distinct id from person", nativeQuery = true)
List<Integer> getIds();
}
# | # |
---|---|
作者: | Cay |
QQ: | |
邮箱: | 412425870@qq.com |
微信公众号:Cay课堂 | |
csdn博客: | http://blog.csdn.net/caychen |
码云: | https://gitee.com/caychen/ |
github: | https://github.com/caychen |
点击群号或者扫描二维码即可加入QQ群:328243383(1群) | |
点击群号或者扫描二维码即可加入QQ群:180479701(2群) |
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。