Spring Data JPA。
是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
<!--添加springdata-jpa依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
基础步骤:
1. 在application.properties中加入
spring.datasource.url=jdbc:mysql://localhost:3306/数据库名 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.username=用户名 spring.datasource.password=密码
2. 建立实体
@Entity 表示这个类是一个实体,即有一个数据库表格和它对应
@Table(name="XX") 用来指定一个实体和哪个表格对应, 其中的name属性是表格名字
@Id 表示这个属性是主键
@Column(name = "PublisherId", nullable = false) 用来指定一个属性和列的对应关系,其中name是列的名字,nullable表示这一列是否允许非空
@GeneratedValue(strategy = GenerationType.IDENTITY) 主键自增
@Basic 默认被添加,表示这个属性是会被进行持久化的
快捷键alt+insert equals and hashCode
3. 建立dao层代码
JPA的命名习惯:实体+Repository(BooksRepository),放入repository包。
之前使用JDBC的时候,命名建议使用BooksDAO,放入dao包
使用MyBatis的时候,命名建议使用BooksMapper,放入mapper包
⚠️:让BooksRepository接口继承CrudRepository接口
CrudRepository接口需要带有范型<Books, Long>,其中第一个参数是实体的类型,第二个参数是主键的类型
import org.springframework.data.repository.CrudRepository; //Spring Data自带的很多方法如果可以满足日常使用,是不需要自己书写方法的。但是有一些特殊查询是JPA没有提供的,就需要我们自己书写 public interface BooksRepository extends CrudRepository<实体类, 主键类型> { }
4.手动的对Spring Data JPA框架进行配置
在config下建立文件
package com.example.jpa.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.persistence.EntityManagerFactory; import javax.persistence.SharedCacheMode; import javax.persistence.ValidationMode; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration @EnableJpaRepositories( //启用JPA和Spring Data的整合 basePackages = "com.example.jpa.repository", //整合后,repository接口所在的位置 entityManagerFactoryRef = "entityManagerFactoryBean", //实体管理器,下面一个方法的名字 transactionManagerRef = "transactionManager") //Spring Data JPA应该让谁来管理事务?事务管理器,下面一个方法的名字 @EnableTransactionManagement //让Spring框架进行事务的管理 public class SpringDataJpaConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(DataSource dataSource) { Map<String, Object> properties = new HashMap<>(); properties.put("javax.persistence.schema-generation.database.action", "none"); HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect"); LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setPackagesToScan("实体包路径"); //扫描实体所在的路径 factory.setJpaVendorAdapter(adapter); factory.setJpaPropertyMap(properties); factory.setDataSource(dataSource); factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE); factory.setValidationMode(ValidationMode.NONE); return factory; } @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory); return txManager; } }
1. JPQL查询
HQL全称是Hibernate Query Language
JPQL全称是Java Persistence Query Language
HQL和JPQL非常类似,同时也比较类似于SQL
我们之所以使用JPQL主要原因是为了和SQL解耦合
public interface BooksRepository extends PagingAndSortingRepository<Books, Long> { @Query(value = "from Books where author = :author and 1=0") List<Books> a(String author); }
⚠️value在这里是不可以省略的
⚠️JPQL/HQL语句中不能出现表格名和列名,必须使用实体名和属性名进行替代
:author 类似于JDBC中的?和MyBatis中的#{author}
2. 原生SQL查询
public interface BooksRepository extends PagingAndSortingRepository<Books, Long> { @Query(value = "select * from books where author = :author", nativeQuery = true) List<Books> b(String author); }
nativeQuery = true 表示这条语句是一个原生查询,即一条SQL语句。
原生查询带来了一定程度的灵活性,可以根据不同的数据库书写一些“特色语句”(比如limit)
但是原生查询也将SQL和JPA耦合的过于紧密,所以不到万不得已的情况下,尽量不要使用原生查询。
3. 命名查询
给一个查询起一个名字,然后将这个查询放在实体上。这种用法在工作中并不常见。
@Entity @NamedQuery(name="Books.a", query = "select b from Books b where b.author = :author") public class Books implements Serializable { //命名查询需要放在实体上,名字要求是“返回值.方法名”(name="Books.a") public interface BooksRepository extends PagingAndSortingRepository<Books, Long> { List<Books> a(String author); }
注意::Spring Data JPA有多种查询方式,如果几种同时使用的时候,优先级是什么?
@Query注解最优先生效 ---> @NamedQuery ---> 按照方法名字的书写来判定规则
分页和排序
select * from books limit 5,2; 从第5条开始往下查询2条。注意数据从第0行开始。
在JPA中实现分页和排序只需要实现PagingAndSortingRepository接口
public interface BooksRepository extends PagingAndSortingRepository<Books, Long> { }
@Test void contextLoads() { booksRepository.findAll(Sort.by(Sort.Direction.DESC, "title")).forEach(System.out::println); //Sort.by()按照升序或者降序进行排序,后面跟上要排序的列 Page<Books> page = booksRepository.findAll(PageRequest.of(0, 4)); //查看第0页,每页里面存放4条结果 //Page里面存放的就是分页后的内容 System.out.println(page.getTotalElements()); //getTotalElements表示结果中一共有多少条元素 System.out.println(page.getTotalPages()); //上面的n条记录,一共被分成了几页 Iterator<Books> books = page.iterator(); //出获得了查询结果的一个迭代器 while(books.hasNext()) System.out.println(books.next()); page = booksRepository.findAll(PageRequest.of(0, 4, Sort.by(Sort.Direction.DESC, "title"))); }
@Temporal:用来设置Date类型的属性,当然随着Date的过时,这个注解已经很少使用了。
@Enumerated:用来映射枚举类型的字段
@Lob:用来映射大对象
@Transinet:作用是被标记的属性不参与持久化。也就是说这个属性和数据库表格里没有对应的列。
@IdClass: 复合主键
1. 建立一个复合主键类,其中包含复合主键的两个属性
public class NameAgeKey implements Serializable { private String name; private Integer age; //无参,带参构造方法,getter/setter }
2. 建立实体: 在复合主键的每一个属性上都加上@Id注解。然后在实体上添加@IdClass(value = 复合主键类.class)
@Entity @Table(name ="xxx") @IdClass(value = NameAgeKey.class) public class Xxx { @Id private String name; @Id private Integer age; //其余属性,无参,带参构造方法,getter/setter }
3. public interface XxxRepository extends CrudRepository<Xxx, NameAgeKey> {}
解决N+1 SQL语句问题
在User实体上加入
@NamedEntityGraph(name = "userEntityGraph", attributeNodes = {@NamedAttributeNode("logs")}) public interface UserRepository extends CrudRepository<User, Integer> { @Override @EntityGraph(value = "userEntityGraph") Iterable<User> findAll(); }
下表描述了JPA支持的关键字以及包含该关键字的JPA命名查询方法:
关键字 |
示例 |
SQL |
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> ages) |
… 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) |