Springboot - 集成 JPA

1.什么是jpa呢?

JPA就是Java Persistence API的意思,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。笔者觉得这个由SpringBoot 提供的JPARepository真的是非常爽。基本上大部分的业务都可以满足了。

Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,包括非关系数据库、Map-Reduce 框架、云数据服务等等;另外也包含对关系数据库的访问支持。 
Spring Data 包含多个子项目:

    • Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化
    • Hadoop - 基于 Spring 的 Hadoop 作业配置和一个 POJO 编程模型的 MapReduce 作业
    • Key-Value - 集成了 Redis 和 Riak ,提供多个常用场景下的简单封装
    • Document - 集成文档数据库:CouchDB 和 MongoDB 并提供基本的配置映射和资料库支持
    • Graph - 集成 Neo4j 提供强大的基于 POJO 的编程模型
    • Graph Roo AddOn - Roo support for Neo4j
    • JDBC Extensions - 支持 Oracle RAD、高级队列和高级数据类型
    • JPA - 简化创建 JPA 数据访问层和跨存储的持久层功能
    • Mapping - 基于 Grails 的提供对象映射框架,支持不同的数据库
    • Examples - 示例程序、文档和图数据库
    • Guidance - 高级文档

2.jpa具有什么优势?

2.1标准化 JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

2.2容器级特性的支持 JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

2.3简单方便 JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。

2.4查询能力 JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

2.5高级特性 JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

3.基于注解的使用

3.1 JPA拥有哪些注解

 
注解 解释
@Entity 声明类为实体或表。
@Table 声明表名。
@Basic 指定非约束明确的各个字段。
@Embedded 指定类或它的值是一个可嵌入的类的实例的实体的属性。
@Id 指定的类的属性,用于识别(一个表中的主键)。
@GeneratedValue 指定如何标识属性可以被初始化,例如自动、手动、或从序列表中获得的值。
@Transient 指定的属性,它是不持久的,即:该值永远不会存储在数据库中。
@Column 指定持久属性栏属性。
@SequenceGenerator 指定在@GeneratedValue注解中指定的属性的值。它创建了一个序列。
@TableGenerator 指定在@GeneratedValue批注指定属性的值发生器。它创造了的值生成的表。
@AccessType 这种类型的注释用于设置访问类型。如果设置@AccessType(FIELD),则可以直接访问变量并且不需要getter和setter,但必须为public。如果设置@AccessType(PROPERTY),通过getter和setter方法访问Entity的变量。
@JoinColumn 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。
@UniqueConstraint 指定的字段和用于主要或辅助表的唯一约束。
@ColumnResult 参考使用select子句的SQL查询中的列名。
@ManyToMany 定义了连接表之间的多对多一对多的关系。
@ManyToOne 定义了连接表之间的多对一的关系。
@OneToMany 定义了连接表之间存在一个一对多的关系。
@OneToOne 定义了连接表之间有一个一对一的关系。
@NamedQueries 指定命名查询的列表。
@NamedQuery 指定使用静态名称的查询。

4.实例

4.1maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

4.2 数据源和JPA配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mytest
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver //驱动
  jpa:
    hibernate:
      ddl-auto: update //自动更新
    show-sql: true  //日志中显示sql语句


jpa.hibernate.ddl-auto是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下: ·
create:每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 ·
create-drop每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,
     即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

4.3创建实体以及数据访问接口

// Person.java

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", nullable = true, length = 20)
    private String name;

    @Column(name = "agee", nullable = true, length = 4)
    private int age;

get ... set... }

接着是 Dao 接口,改接口只需要继承JpaRepository接口即可。

public interface PersonRepository extends JpaRepository<Person, Long> {
}

或者注解的方式

@RepositoryDefinition(domainClass = Person.class, idClass = Integer.class)
public interface PersonRepository {

}

写一个rest接口以供测试使用

@RestControllerpublic class PerconController {

    @Autowired
    private PersonRepository personRepository;

    @PostMapping(path = "addPerson")
    public void addPerson(Person person) {
        personRepository.save(person);
    }

    @DeleteMapping(path = "deletePerson")
    public void deletePerson(Long id) {
        personRepository.delete(id);
    }
}

4.4编写自己的方法

4.4.1 在repository接口中添加如下查询方法

Person findByName(String name);

4.4.2 rest接口调用

public Object getPerson(@QueryParam("name") String name) {
        return personRepository.findByName(name);
    }

4.4.3 那么JPA是通过什么规则来根据方法名生成sql语句查询的呢

其实JPA在这里遵循Convention over configuration(约定大约配置)的原则,遵循spring 以及JPQL定义的方法命名。Spring提供了一套可以通过命名规则进行查询构建的机制。这套机制会把方法名首先过滤一些关键字,比如 find…By, read…By, query…By, count…By 和 get…By 。系统会根据关键字将命名解析成2个子语句,第一个 By 是区分这两个子语句的关键词。这个 By 之前的子语句是查询子语句(指明返回要查询的对象),后面的部分是条件子语句。如果直接就是 findBy… 返回的就是定义Respository时指定的领域对象集合,同时JPQL中也定义了丰富的关键字:and、or、Between等等,下面我们来看一下JPQL中有哪些关键字:

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals 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 ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection 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)

4.5 自己编写查询语句。

 如果是一些很复杂的查询,那么方法名就会变得很长很长,读起来很辛苦。可以通过注解的方式,将自己写的SQL语句传进去。

@RepositoryDefinition(domainClass = Student.class, idClass = Integer.class)
public interface StudentRepository {
    //注意,这里from后的不是数据表名,而是对于的实体类名
    @Query("select o from Student o where id=(select max(id) from Student t1)")
    public Student getStudentByMaxId();
//这里的?是占位符,在执行是会替换成对应位置的参数,比如这里?1会被替换成name的值 @Query("select o from Student o where o.name=?1 and o.age=?2") public List<Student> queryParams1(String name, Integer age);
//这里的:+参数名则是绑定参数,在参数列中使用@Param注解绑定参数,则可在SQL语句中使用:+参数名得到相应参数的值 @Query("select o from Student o where o.name=:name and o.age=:age") public List<Student> queryParams2(@Param("name")String name, @Param("age")Integer age); }

使用@Quer注解传入SQL语句,我们就可以实现一些比较复杂的查询。除了查询,我们平时还会有更新、插入、删除的操作。

要实现插入、更新、删除操作,我们只需要再多加一个注解@Modifying,告诉框架,它是一个DML操作即可。

@RepositoryDefinition(domainClass = Student.class, idClass = Integer.class)
public interface StudentRepository {
    @Modifying
    @Query("update Student o set o.age = :age where o.id = :id")
    public void update(@Param("id")Integer id, @Param("age")Integer age);
}

又或许你必须要使用原生的SQL语句怎么办?只需要在@Query注解中设置nativeQuery即可

@RepositoryDefinition(domainClass = Student.class, idClass = Integer.class)
public interface StudentRepository {
    //注意,这里使用原生的SQL语句,from后的表名应该是数据库表名
    @Query(nativeQuery = true, value = "select count(1) from student")
    public long getCount();
}

5.声明式事务

 SpringBoot默认已经帮我们自动配置好了JPATransaction,我们直接使用即可。

直接在Service层测试@Transactional,当然正常的事务隔离级别还有事务传递方式还是和以前Spring一样配置。注意这个@Transactional是Spring提供的,不要错误的使用JPA的@Transactional

以上就是jpa的简单实用和介绍。

猜你喜欢

转载自www.cnblogs.com/appleat/p/9259826.html