springboot Jpa解析

1 JPA是什么

JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.
JPA由三个不同的组件构成:

  • 实体(Entities): 在当前版本的JPA中实体是普通Java对象(POJO)。老版本的JPA中实体类需要继承JPA提供的实体基类,但是这样的设计导致框架中存在了严重的依赖关系,测试变得更加困难;所以在新版JPA中不再要求实体类继承任何框架类。
  • 对象-关系型元数据(Object-relational metadata): 应用程序的开发者们必须正确设定Java类和它们的属性与数据库中的表和列的映射关系。有两种设定方式:通过特定的配置文件建立映射;或者使用在新版本中支持的注解.
  • Java持久化查询语句(Java Persistence Query Language - JPQL): 因为JPA旨在建立不依赖于特定的数据库的抽象层,所以它也提供了一种专有查询语言来代替SQL。 这种由JPQL到SQL语言的转换,为JPA提供了支持不同数据库方言的特性,使得开发者们在实现查询逻辑时不需要考虑特定的数据库类型。

2 JPA配置

如下更新pom.xml,将项目中需要的类库添加到依赖(dependencies)设置部分:


    7.0
    1.3.176
    4.3.8.Final



    
        javax
        javaee-api
        ${jee.version}
        provided
    
    
        com.h2database
        h2
        ${h2.version}
    
    
        org.hibernate
        hibernate-entitymanager
        ${hibernate.version}
    

3 数据库表(Tables)

@Entity
@Table(name = "T_PERSON")
public class Person {
    private Long id;
    private String firstName;
    private String lastName;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column(name = "FIRST_NAME")
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Column(name = "LAST_NAME")
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

附加的注解@Table是可选的,示例中,我们想要所有的表都有前缀T_,所以我们设定了表名T_PERSON。表T_PERSON有3列:ID,FIRST_NAME,LAST_NAME。
这些信息是通过注解@Column和它的属性name提供给JPA中所有具有setter和getter方法的属性创建数据库列,更多的信息:

@Column(name = "FIRST_NAME", length = 100, nullable = false, unique = false)

上例中的代码指明了:限制这个字符串长度为100个字符;该列不能包含空值(null);不必是唯一的。如果试图将空值(null)作为first name插入数据库表的话,就会触发数据库约束冲突,进而导致当前事务回滚。
注解@Id和@GeneratedValue用于告诉JPA该值是主键,而且会自动生成。

运行上面的代码,Hibernate会在我们的本地H2数据库上执行如下操作:

Hibernate: drop table T_PERSON if exists
Hibernate: create table T_PERSON (id bigint generated by default as identity, FIRST_NAME varchar(255), LAST_NAME varchar(255), primary key (id))
Hibernate: insert into T_PERSON (id, FIRST_NAME, LAST_NAME) values (null, ?, ?)

4 继承(Inheritance)

完成项目的设置和上一节中的简单用例之后,我们来看一些更加复杂的用例。假设我们现在想要存储Geek们的个人信息以及他们最喜爱的编程语言信息。由于Geek也是Person,所以我们在Java模式实现中看到它是Person的子类:

@Entity
@Table(name = "T_GEEK")
public class Geek extends Person {
    private String favouriteProgrammingLanguage;
    private List projects = new ArrayList();

    @Column(name = "FAV_PROG_LANG")
    public String getFavouriteProgrammingLanguage() {
            return favouriteProgrammingLanguage;
    }

    public void setFavouriteProgrammingLanguage(String favouriteProgrammingLanguage) {
        this.favouriteProgrammingLanguage = favouriteProgrammingLanguage;
    }
}

首先在Geek类设置注解@Entity和@Table;然后使用Hibernate创建新表T_GEEK:

Hibernate: create table T_GEEK (DTYPE varchar(31) not null, id bigint generated by default as identity, FIRST_NAME varchar(255), LAST_NAME varchar(255), FAV_PROG_LANG varchar(255), primary key (id))

我们可以看到Hibernate为这两个实体创建了一个表;新增的名为DTYPE的列用于存储标志位,标识我们存储的是Person还是Geek。我们来添加一些用于持久化Geek到数据库的逻辑(为了更好的可读性,我省略了捕获异常和回滚事务的代码):

private void persistGeek(EntityManager entityManager) {
    EntityTransaction transaction = entityManager.getTransaction();
    transaction.begin();
    Geek geek = new Geek();
    geek.setFirstName("Gavin");
    geek.setLastName("Coffee");
    geek.setFavouriteProgrammingLanguage("Java");
    entityManager.persist(geek);
    geek = new Geek();
    geek.setFirstName("Thomas");
    geek.setLastName("Micro");
    geek.setFavouriteProgrammingLanguage("C#");
    entityManager.persist(geek);
    geek = new Geek();
    geek.setFirstName("Christian");
    geek.setLastName("Cup");
    geek.setFavouriteProgrammingLanguage("Java");
    entityManager.persist(geek);
    transaction.commit();
}

这个方法执行后,表T_PERSON中含有如下记录(也包含我们之前已经插入的Person记录):

sql> select * from t_person;
DTYPE  | ID | FIRST_NAME | LAST_NAME | FAV_PROG_LANG
Person | 1  | Homer      | Simpson   | null
Geek   | 2  | Gavin      | Coffee    | Java
Geek   | 3  | Thomas     | Micro     | C#
Geek   | 4  | Christian  | Cup       | Java

5 实体关系(Relationships)

5.1. 一对一(OneToOne)

@Entity
@Table(name = "T_ID_CARD")
public class IdCard {
    private Long id;
    private String idNumber;
    private Date issueDate;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column(name = "ID_NUMBER")
    public String getIdNumber() {
        return idNumber;
    }

    public void setIdNumber(String idNumber) {
        this.idNumber = idNumber;
    }

    @Column(name = "ISSUE_DATE")
    @Temporal(TemporalType.TIMESTAMP)
    public Date getIssueDate() {
        return issueDate;
    }

    public void setIssueDate(Date issueDate) {
        this.issueDate = issueDate;
    }
}

下面定义告诉JPA每个Person含有一个确定的IDCard:

@Entity
@Table(name = "T_PERSON")
public class Person {
    ...
    private IdCard idCard;
    ...

    @OneToOne
    @JoinColumn(name = "ID_CARD_ID")
    public IdCard getIdCard() {
        return idCard;
    }

一对多可以自行查阅文档。
下面举几个例子来看一下如何使用jpa查询

Page findALL(Pageable pageable);
Page findByUserName(String userName,Pageable pageable);

@Test
public void testPageQuery() throws Exception {
    int page=1,size=10;
    Sort sort = new Sort(Direction.DESC, "id");
    Pageable pageable = new PageRequest(page, size, sort);
    userRepository.findALL(pageable);
    userRepository.findByUserName("testName", pageable);
}

上面展示了一个简单的分页查询。
在SQL的查询方法上面使用@Query注解,如涉及到删除和修改在需要加上@Modifying.也可以根据需要添加 @Transactional 对事物的支持,查询超时的设置等

@Modifying
@Query("update User u set u.userName = ?1 where c.id = ?2")
int modifyByIdAndUserId(String  userName, Long id);
    
@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteByUserId(Long id);
  
@Transactional(timeout = 10)
@Query("select u from User u where u.emailAddress = ?1")
    User findByEmailAddress(String emailAddress);

通过查询学生获取班级信息

public class StudentBadDto {

    private Student stu;
    private Classroom cla;
    public StudentBadDto(Student stu, Classroom cla) {
        super();
        this.stu = stu;
        this.cla = cla;
    }

  //省略了getter和setter方法
}

查询代码

@Query("select new org.konghao.model.StudentBadDto(stu,cla) from Student stu,Classroom cla where stu.cid=cla.id")
public List listBadStu();

@Test
public void testListStu() {
    List sds = studentRepository.listBadStu();
    Assert.assertEquals(5, sds.size());
    Assert.assertEquals(2, sds.get(0).getStu().getId());
    Assert.assertEquals(1, sds.get(0).getCla().getId());
}

下面是发出的sql

image.png

下面给出一个完整的查询方案

public interface PersonRepository extends Repository {

  List findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // Enables the distinct flag for the query
  List findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // Enabling ignoring case for an individual property
  List findByLastnameIgnoreCase(String lastname);
  // Enabling ignoring case for all suitable properties
  List findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // Enabling static ORDER BY for a query
  List findByLastnameOrderByFirstnameAsc(String lastname);
  List findByLastnameOrderByFirstnameDesc(String lastname);
}

查询方法的结果可以通过关键字first或者top来限制,它们可以交替使用。在top/firest后添加数字来表示返回最大的结果数。如果没有数字,则默认假定1作为结果大小。

User findFirstByOrderByLastnameAsc();

     User findTopByOrderByAgeDesc();

     Page queryFirst10ByLastname(String lastname, Pageable pageable);

     Slice findTop3ByLastname(String lastname, Pageable pageable);

     List findFirst10ByLastname(String lastname, Sort sort);

     List findTop10ByLastname(String lastname, Pageable pageable);

下回有时间我会从底层对jap进行讲解,期待下一次把。。。

你可能感兴趣的:(springboot Jpa解析)