Spring JPA

1. 初步使用

  • (1). pom.xml 中引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
1
2
3
4
  • (2). application.properties 中配置数据源
spring.datasource.url=jdbc:postgresql://192.168.1.100:54321/postgres
spring.datasource.username=postgres
spring.datasource.password=123456

spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect

## This is important
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=validate

# The exception(java.lang.reflect.InvocationTargetException: null) is not a real exception because
# Hibernate just tries to get some meta information from the database when loading.
# So you can use spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false to disable it.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • (3). 创建 UsersEntity (与数据库对应)
@Entity
@Table(name = "users")
public class Users {

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

    private String username;

    private String password;

    private Boolean enabled;

    //...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • (4). 创建 UsersRepository
public interface UsersRepository extends JpaRepository<Users, Integer> {
}
1
2
  • 接口的继承关系: JpaRepository -> PagingAndSortingRepository -> CrudRepository -> Repository
  • (5). 使用 UserRepository 进行数据操作
    //...

    @Autowired
    private UsersRepository usersRepository;

    @GetMapping("/test")
    public String test(){
        Users user = new Users();
        user.setUsername("u1");
        user.setPassword("1234");
        user.setEnabled(true);
        usersRepository.save(user);

        List<Users> users = usersRepository.findAll();
        System.out.println(users.size());

        for(Users u: users){
            System.out.println(u.toString());
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

2. @Query

@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);

@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);

@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname,
                                @Param("firstname") String firstname);
1
2
3
4
5
6
7
8
9

3. JpaSpecificationExecutor<T> 接口

public interface UserRepository extends JpaRepository<User, Integer>,
                                        JpaSpecificationExecutor<User> {
}

// 使用示例: (按页面查询,并使用复杂的查询条件)
Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");
Sort sort = new Sort(order);

Pageable pageable = new PageRequest(0, 5, sort);

Specification<User> specification = new Specification<User>(){
    @Override
    public Predicate toPredicate(Root<User> root,
                                CriteriaQuery<?> query,
                                CriteriaBuilder cb){
        // age is greater than 50
        Path path = root.get("age");
        return cb.gt(path, 50);
    }
}

Page<User> page = userRepository.findAll(specification, pageable);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

4. @OneToMany 与 @ManyToOne

(1). 简介

一对多、多对一这种双向关系是最常见的数据表关系。如:一条用户数据,可以对应多条授权数据。一条授权数据,只对应一条用户数据。

具体情况如下:

-- postgre.sql
CREATE TABLE usr (
    id SERIAL PRIMARY KEY,
    username VARCHAR(256) NOT NULL UNIQUE,
    password VARCHAR(1024) NOT NULL,
    active boolean NOT NULL DEFAULT TRUE
);

CREATE TABLE authority (
    id SERIAL PRIMARY KEY,
    username VARCHAR(256) NOT NULL REFERENCES usr(username),
    authority VARCHAR(256) NOT NULL
);
1
2
3
4
5
6
7
8
9
10
11
12
13

小贴士

  • authority 对于 usr,是多对一(@ManyToOne
  • usr 对于 authority,是一对多(@OneToMany
  • @ManyToOne 的一方,即 authority,是关系的维护方(Owner Side),关联关系的外键定义在这个表里的(即 username 列)。
  • @OneToMany 的一方,即 usr,是关系的被维护方(Reverse Side),表中不需要定义任何与 authority 有关的关系。

(2). JPA 实现

@Entity
@Table(name = "usr")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String username;
    private String password;
    private Boolean active;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Authority> authorities = new HashSet<>();

    public void addAuthority(Authority authority){
        this.authorities.add(authority);
        authority.setUser(this);
    }
    
    public void removeAuthority(Authority authority){
        this.authorities.remove(authority);
        authority.setUser(null);
    }
 
    //...
}

@Entity
@Table(name = "authority")
public class Authority {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String authority;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "username", referencedColumnName = "username")
    private User user;

    //...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

注释

  • @OneToManymappedBy="user",表示类之间为双向关系,它的值是,"多"方(即 Authority 类)中定义的"一"方(即 User 类)的变量名(即 private User user 中的 user

  • @JoinColumn,表示方(即 Authority 类)中定义为关联的外键。

  • fetch = FetchType.Lazy,表示当主类(如:Authority)加载时不加载关系类(如:User),等访问时才加载。如果选 FetchType.EAGER,则加载即同时加载关系类,性能变差。默认值为 FetchType.LAZY

  • CascadeType.ALL,表示主类(如:User)与关系类(如:Authority)的级联关系,当修改到主类时,是否要影响到关系类。级联关系有如下几种:

    • CascadeType.PERSIST 新建
    • CascadeType.REMOVE 删除
    • CascadeType.REFRESH 刷新
    • CascadeType.MERGE 更新
    • CascadeType.ALL 以上所有
  • orphanRemoval,如果设置为 true,当关系被断开时,方实体(即 Authority)将被删除。

  • 需要定义 addAuthority()removeAuthority() 两个方法。

参考资料:https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/

最近更新: 8/18/2019, 10:18:22 PM