Spring-data-jpa

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)

原文地址:https://www.cnblogs.com/429lirui/p/13559195.html