SpringJAP

Spring Data JPA

JPA 重要的是实战,这里仅对小部分知识点进行总结。

如何使用 JPA 在数据库中非持久化一个字段?

假如我们有下面一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity(name="USER")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Long id;

@Column(name="USER_NAME")
private String userName;

@Column(name="PASSWORD")
private String password;

private String secrect;
}

如果我们想让secrect 这个字段不被持久化,也就是不被数据库存储怎么办?我们可以采用下面几种方法:

1
2
3
4
5
static String transient1; // not persistent because of static
final String transient2 = "Satish"; // not persistent because of final
transient String transient3; // not persistent because of transient
@Transient
String transient4; // not persistent because of @Transient

一般使用后面两种方式比较多,我个人使用注解的方式比较多。

JPA 的审计功能是做什么的?有什么用?

审计功能主要是帮助我们记录数据库操作的具体行为比如某条记录是谁创建的、什么时间创建的、最后修改人是谁、最后修改时间是什么时候。

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
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {

@CreatedDate
@Column(updatable = false)
@JsonIgnore
private Instant createdAt;

@LastModifiedDate
@JsonIgnore
private Instant updatedAt;

@CreatedBy
@Column(updatable = false)
@JsonIgnore
private String createdBy;

@LastModifiedBy
@JsonIgnore
private String updatedBy;
}

@CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值

@CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值

@LastModifiedDate@LastModifiedBy同理。

实体之间的关联关系注解有哪些?

  • @OneToOne : 一对一。
  • @ManyToMany:多对多。
  • @OneToMany : 一对多。
  • @ManyToOne:多对一。

利用 @ManyToOne@OneToMany 也可以表达多对多的关联关系。

SpringBoot JAP

创建表

@Entity声明一个类对应一个数据库实体。

@Table 设置表名

1
2
3
4
5
6
7
8
9
10
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
省略getter/setter......
}

创建主键

@Id:声明一个字段为主键。

使用@Id声明之后,我们还需要定义主键的生成策略。我们可以使用 @GeneratedValue 指定主键生成策略。

1.通过 @GeneratedValue直接使用 JPA 内置提供的四种主键生成策略来指定主键生成策略。

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

JPA 使用枚举定义了 4 种常见的主键生成策略,如下:

Guide:枚举替代常量的一种用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum GenerationType {

/**
* 使用一个特定的数据库表格来保存主键
* 持久化引擎通过关系数据库的一张特定的表格来生成主键,
*/
TABLE,

/**
*在某些数据库中,不支持主键自增长,比如Oracle、PostgreSQL其提供了一种叫做"序列(sequence)"的机制生成主键
*/
SEQUENCE,

/**
* 主键自增长
*/
IDENTITY,

/**
*把主键生成策略交给持久化引擎(persistence engine),
*持久化引擎会根据数据库在以上三种主键生成 策略中选择其中一种
*/
AUTO
}
1
@GeneratedValue`注解默认使用的策略是`GenerationType.AUTO
1
2
3
4
5
public @interface GeneratedValue {

GenerationType strategy() default AUTO;
String generator() default "";
}

一般使用 MySQL 数据库的话,使用GenerationType.IDENTITY策略比较普遍一点(分布式系统的话需要另外考虑使用分布式 ID)。

通过 @GenericGenerator声明一个主键策略,然后 @GeneratedValue使用这个策略

1
2
3
4
@Id
@GeneratedValue(generator = "IdentityIdGenerator")
@GenericGenerator(name = "IdentityIdGenerator", strategy = "identity")
private Long id;

等价于:

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

jpa 提供的主键生成策略有如下几种:

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
public class DefaultIdentifierGeneratorFactory
implements MutableIdentifierGeneratorFactory, Serializable, ServiceRegistryAwareService {

@SuppressWarnings("deprecation")
public DefaultIdentifierGeneratorFactory() {
register( "uuid2", UUIDGenerator.class );
register( "guid", GUIDGenerator.class ); // can be done with UUIDGenerator + strategy
register( "uuid", UUIDHexGenerator.class ); // "deprecated" for new use
register( "uuid.hex", UUIDHexGenerator.class ); // uuid.hex is deprecated
register( "assigned", Assigned.class );
register( "identity", IdentityGenerator.class );
register( "select", SelectGenerator.class );
register( "sequence", SequenceStyleGenerator.class );
register( "seqhilo", SequenceHiLoGenerator.class );
register( "increment", IncrementGenerator.class );
register( "foreign", ForeignGenerator.class );
register( "sequence-identity", SequenceIdentityGenerator.class );
register( "enhanced-sequence", SequenceStyleGenerator.class );
register( "enhanced-table", TableGenerator.class );
}

public void register(String strategy, Class generatorClass) {
LOG.debugf( "Registering IdentifierGenerator strategy [%s] -> [%s]", strategy, generatorClass.getName() );
final Class previous = generatorStrategyToClassNameMap.put( strategy, generatorClass );
if ( previous != null ) {
LOG.debugf( " - overriding [%s]", previous.getName() );
}
}
}

设置字段类型

@Column 声明字段。

示例:

设置属性 userName 对应的数据库字段名为 user_name,长度为 32,非空

1
2
@Column(name = "user_name", nullable = false, length=32)
private String userName;

设置字段类型并且加默认值,这个还是挺常用的。

1
2
@Column(columnDefinition = "tinyint(1) default 1")
private Boolean enabled;

指定不持久化特定字段

@Transient:声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库 。

如果我们想让secrect 这个字段不被持久化,可以使用 @Transient关键字声明。

1
2
3
4
5
6
7
8
@Entity(name="USER")
public class User {

......
@Transient
private String secrect; // not persistent because of @Transient

}

除了 @Transient关键字声明, 还可以采用下面几种方法:

1
2
3
static String secrect; // not persistent because of static
final String secrect = "Satish"; // not persistent because of final
transient String secrect; // not persistent because of transient

一般使用注解的方式比较多

声明大字段

@Lob:声明某个字段为大字段。

1
2
@Lob
private String content;

更详细的声明:

1
2
3
4
5
6
@Lob
//指定 Lob 类型数据的获取策略, FetchType.EAGER 表示非延迟加载,而 FetchType.LAZY 表示延迟加载 ;
@Basic(fetch = FetchType.EAGER)
//columnDefinition 属性指定数据表对应的 Lob 字段类型
@Column(name = "content", columnDefinition = "LONGTEXT NOT NULL")
private String content;

创建枚举类型的字段

可以使用枚举类型的字段,不过枚举字段要用@Enumerated注解修饰。

1
2
3
4
5
6
7
8
9
public enum Gender {
MALE("男性"),
FEMALE("女性");

private String value;
Gender(String str){
value=str;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@Enumerated(EnumType.STRING)
private Gender gender;
省略getter/setter......
}

数据库里面对应存储的是 MALE/FEMALE。

增加审计功能

只要继承了 AbstractAuditBase的类都会默认加上下面四个字段。

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
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {

@CreatedDate
@Column(updatable = false)
@JsonIgnore
private Instant createdAt;

@LastModifiedDate
@JsonIgnore
private Instant updatedAt;

@CreatedBy
@Column(updatable = false)
@JsonIgnore
private String createdBy;

@LastModifiedBy
@JsonIgnore
private String updatedBy;
}

我们对应的审计功能对应地配置类可能是下面这样的(Spring Security 项目):

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableJpaAuditing
public class AuditSecurityConfiguration {
@Bean
AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}
}

简单介绍一下上面涉及到的一些注解:

  1. @CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值

  2. @CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值

    @LastModifiedDate@LastModifiedBy同理。

@EnableJpaAuditing:开启 JPA 审计功能。

删除/修改数据

@Modifying 注解提示 JPA 该操作是修改操作,注意还要配合@Transactional注解使用。

1
2
3
4
5
6
7
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

@Modifying
@Transactional(rollbackFor = Exception.class)
void deleteByUserName(String userName);
}

关联关系

  • @OneToOne 声明一对一关系
  • @OneToMany 声明一对多关系
  • @ManyToOne 声明多对一关系
  • @ManyToMany 声明多对多关

SpringJAP
http://example.com/2024/01/20/Spring+MyBatis/SpringJPA/
作者
PALE13
发布于
2024年1月20日
许可协议