2.5. @Configuration
一般用来声明配置类,可以使用 @Component注解替代,不过使用@Configuration注解声明配置类更加语义化。
————————@Configuration可以完全用@Component替代吗
虽然@Configuration和@Component在Spring中都可以用来声明一个类为Spring容器的一个bean,但是它们之间有一些关键的区别,特别是在处理bean定义和配置类时。
@Configuration vs@Component
1.@Configuration
- **语义化**:@Configuration明确表示这个类是一个配置类,用于定义bean和配置Spring容器。
- **全局单例**:@Configuration类中的bean方法会被CGLIB代理,以确保这些方法返回的bean是单例的,即使在同一个配置类中多次调用这些方法,也会返回同一个bean实例。这称为**Full Mode**。
- **配置增强**:@Configuration可以与其他Spring配置注解(如@PropertySource、@Import等)一起使用,以更好地组织和管理Spring应用程序的配置。
2.@Component
- **通用性**:@Component是一个通用注解,用于标记一个类为Spring管理的bean。它没有特定的语义,可以用于任何需要被Spring管理的类。
- **局部单例**:如果将@Configuration替换为@Component,方法返回的bean将不会被CGLIB代理,这意味着在同一个类中多次调用这些方法时,每次都会返回一个新的bean实例。这称为**Lite Mode**。
示例对比
使用@Configuration
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyController myController() {
return new MyController(myService());
}
}
在这个例子中,myService()方法会被CGLIB代理,确保在myController()方法中调用时返回同一个MyService实例。
使用@Component
java
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class AppConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyController myController() {
return new MyController(myService());
}
}
在这个例子中,myService()方法不会被CGLIB代理,因此在myController()方法中调用时,每次都会返回一个新的MyService实例。
================
一定一定不要忘记在类上加上 @Validated 注解了,这个参数可以告诉 Spring 去校验方法参数。
-------就用@valid就行了吧,不一定非要同时在类上加@Validated吧
@Valid和@Validated在Spring中都有用于校验的方法参数或类字段的功能,但它们在使用场景和功能上有一些区别。
@Valid vs@Validated
@Valid
- **标准注解**:@Valid是Java Bean Validation(JSR-380)的标准注解,用于触发标准的Bean Validation机制。
- **嵌套校验**:@Valid可以触发嵌套校验,即如果一个对象包含另一个对象,@Valid可以触发对嵌套对象的校验。
- **常用于方法参数**:通常用于方法参数校验和类字段校验。
@Validated
- **Spring特定注解**:@Validated是Spring特定的注解,提供了分组校验的功能。
- **分组校验**:@Validated可以指定校验组,从而对不同场景下的校验规则进行分组和管理。
- **类级别和方法级别**:可以在类级别和方法级别使用,特别是在AOP切面中进行参数校验时非常有用。
示例对比
@Valid 示例
用于方法参数的校验:
java
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class MyController {
public void createUser(@Valid @NotNull User user) {
// 方法实现
}
}
用于嵌套对象的校验:
java
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
public class User {
@NotNull
private String name;
@Valid
private Address address;
}
public class Address {
@NotNull
private String street;
}
@Validated 示例
用于分组校验:
java
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
@Validated
public class MyService {
public void createUser(@Validated(CreateGroup.class) User user) {
// 方法实现
}
}
public class User {
@NotNull(groups = CreateGroup.class)
private String name;
}
public interface CreateGroup {}
用于类级别的校验:
java
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
@Validated
public class MyService {
public void createUser(@NotNull User user) {
// 方法实现
}
}
总结
- **@Valid**@Valid:适用于标准的Bean Validation,常用于方法参数和嵌套对象的校验。如果只是需要基本的校验功能,@Valid通常就足够了。
@Validated:适用于需要分组校验或在类级别进行校验的场景。如果您的校验逻辑需要分组管理或者需要在类级别进行全局的校验,使用@Validated会更合适
===========
分组校验啥意思
分组校验是Java Bean Validation(JSR-380)中的一个功能,允许您根据不同的场景对同一个对象进行不同的校验。通过分组校验,您可以定义不同的校验组,并在不同的场景中选择性地应用这些校验规则。这在需要对同一个对象在不同的操作(如创建、更新、删除)中进行不同的校验时非常有用。
分组校验的基本概念
1. **校验组(Validation Group)**:一个标记接口,用于标识一组校验规则。
2. **分组注解(Group Annotation)**:在校验注解上指定校验组,以便在特定的场景中应用这些校验规则。
3. **分组校验(Group Validation)**:在方法参数或类上指定校验组,以便在调用时触发特定的校验规则。
示例
1. 定义校验组
首先,定义两个校验组接口:
java
public interface CreateGroup {}
public interface UpdateGroup {}
2. 在Bean中使用校验组
在需要校验的Bean类中,使用校验注解并指定校验组:
java
import javax.validation.constraints.NotNull;
public class User {
@NotNull(groups = CreateGroup.class)
private String name;
@NotNull(groups = UpdateGroup.class)
private String id;
// getters and setters
}
在这个例子中:
-name字段在CreateGroup校验组中是必需的。
-id字段在UpdateGroup校验组中是必需的。
3. 在服务类中使用分组校验
在服务类的方法中,使用@Validated注解指定校验组:
java
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
@Validated
public class UserService {
public void createUser(@Validated(CreateGroup.class) User user) {
// 创建用户的逻辑
}
public void updateUser(@Validated(UpdateGroup.class) User user) {
// 更新用户的逻辑
}
}
在这个例子中:
-createUser方法会触发CreateGroup校验组中的校验规则。
-updateUser方法会触发UpdateGroup校验组中的校验规则。
使用场景
分组校验非常适合以下场景:
1. **
==============
设置属性 userName 对应的数据库字段名为 user_name,长度为 32,非空@Column(name = "user_name", nullable = false, length=32)
private String userName;
设置字段类型并且加默认值,这个还是挺常用的。@Column(columnDefinition = "tinyint(1) default 1")
private Boolean enabled;
------
columnDefinition 是啥
columnDefinition是JPA(Java Persistence API)中的一个属性,用于在实体类的字段上指定数据库列的详细定义。它允许您直接在实体类中定义列的SQL类型、默认值、约束等,这些定义会在生成数据库表时被应用。
columnDefinition 的用法
columnDefinition属性允许您在实体类的字段上指定自定义的SQL片段,用于定义该字段在数据库中的具体细节。以下是一些常见的用法示例:
1. 指定列类型和默认值
例如,您希望在数据库中将enabled字段定义为TINYINT(1)类型,并设置默认值为1:
java
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
@Id
private Long id;
@Column(name = "user_name", nullable = false, length = 32)
private String userName;
@Column(columnDefinition = "TINYINT(1) DEFAULT 1")
private Boolean enabled;
// getters and setters
}
在这个例子中,enabled字段在数据库中将被创建为TINYINT(1)类型,并且默认值为1。
2. 指定列类型和其他约束
例如,您希望在数据库中将age字段定义为INT类型,并且不允许为NULL:
java
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
@Id
private Long id;
@Column(name = "user_name", nullable = false, length = 32)
private String userName;
@Column(columnDefinition = "INT NOT NULL")
private Integer age;
// getters and setters
}
在这个例子中,age字段在数据库中将被创建为INT类型,并且不允许为NULL。
使用注意事项
- **数据库依赖**:columnDefinition的内容是数据库特定的,这意味着它可能依赖于您使用的具体数据库管理系统(如MySQL、PostgreSQL等)。在不同的数据库中,列定义的语法可能有所不同。
- **可移植性**:由于columnDefinition直接使用SQL片段,它可能会影响应用程序的数据库可移植性。如果您计划在不同的数据库之间切换,使用columnDefinition时需要特别注意。
总结
==============
可以使用枚举类型的字段,不过枚举字段要用@Enumerated注解修饰。public enum Gender {
MALE("男性"),
FEMALE("女性");
private String value;
Gender(String str){
value=str;
}
}
@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。
------
对应的数据库里面字段类型也是枚举类型吗?不推荐数据库用枚举类型吗?
在JPA中使用枚举类型字段时,数据库中的对应字段类型通常不是数据库本身的枚举类型,而是字符串或整数。这是因为JPA通过@Enumerated注解来控制如何将枚举类型映射到数据库字段。
@Enumerated 注解
@Enumerated注解用于指定枚举类型在数据库中的存储方式。它有两个可选值:
1. **EnumType.STRING**:将枚举的名称(如MALE,FEMALE)存储为字符串。
2. **EnumType.ORDINAL**:将枚举的序数(即枚举在定义时的顺序,从0开始)存储为整数。
示例
1. 使用EnumType.STRING
在数据库中存储枚举的名称:
java
import javax.persistence.*;
public enum Gender {
MALE("男性"),
FEMALE("女性");
private String value;
Gender(String str){
value = str;
}
}
@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;
// Getters and setters
}
在这种情况下,数据库中的gender字段将存储为字符串,例如'MALE'或'FEMALE'。
2. 使用EnumType.ORDINAL
在数据库中存储枚举的序数:
java
@Entity
@Table(name = "role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@Enumerated(EnumType.ORDINAL)
private Gender gender;
// Getters and setters
}
在这种情况下,数据库中的gender字段将存储为整数,例如0(对应MALE)或1(对应FEMALE)。
数据库中的枚举类型
虽然某些数据库支持枚举类型(如PostgreSQL),但在实际应用中,使用数据库枚举类型并不是非常推荐,原因如下:
1. **可移植性**:不同数据库对枚举类型的支持和实现方式不同,使用数据库枚举类型会降低应用程序的数据库可移植性。
2. **灵活性**:数据库枚举类型在添加、删除或修改枚
=============
只要继承了 AbstractAuditBase的类都会默认加上下面四个字段。
@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 项目):
@Configuration
@EnableJpaAuditing
public class AuditSecurityConfiguration {
@Bean
AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}
}
简单介绍一下上面涉及到的一些注解:@CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值@CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值@LastModifiedDate、@LastModifiedBy同理。@EnableJpaAuditing:开启 JPA 审计功能。
这个怎么用的,具体插入到数据库那个字段呢?
3. 如何使用(续)
继承AbstractAuditBase 类
任何实体类继承了AbstractAuditBase类后,都会自动拥有审计字段,并且在插入和更新时,这些字段会自动填充。
java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User extends AbstractAuditBase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and setters
}
4. 数据库中的字段
当您继承了AbstractAuditBase类并在数据库中创建相应的表时,表结构将自动包含审计字段。以User实体为例,数据库表user可能会包含以下字段:
-id:用户ID
-name:用户名
-created_at:创建时间
-updated_at:最后修改时间
-created_by:创建人
-updated_by:最后修改人
5. 插入和更新操作
当您对继承了AbstractAuditBase的实体类进行插入和更新操作时,Spring Data JPA会自动填充这些审计字段。例如:
插入操作
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(String name) {
User user = new User();
user.setName(name);
return userRepository.save(user);
}
}
在插入操作中,createdAt、createdBy、updatedAt和updatedBy字段会被自动填充。例如,createdAt和updatedAt会被设置为当前时间,createdBy和updatedBy会被设置为当前登录的用户名。
更新操作
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User updateUser(Long id, String name) {
User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
user.setName(name);
return userRepository.save(user);
}
}
在更新操作中,updatedAt和updatedBy字段会被自动更新为当前时间和当前登录的用户名,而created
==========
我必须在建表的时候添加created_at等等字段吗?还是它会自动添加字段?
在使用Spring Data JPA和Hibernate时,实体类中的字段映射到数据库表的列通常是自动处理的。这意味着,当您定义了包含审计字段的实体类并使用JPA/Hibernate进行数据库表创建时,这些字段会自动添加到相应的数据库表中。
自动添加字段
如果您启用了自动DDL(数据定义语言)生成功能,Hibernate会根据您的实体类自动创建或更新数据库表结构,包括添加审计字段。自动DDL生成功能通常由spring.jpa.hibernate.ddl-auto属性控制。
示例配置
在application.properties或application.yml中配置自动DDL生成:
properties
spring.jpa.hibernate.ddl-auto=update
或者在application.yml中:
yaml
spring:
jpa:
hibernate:
ddl-auto: update
自动DDL生成选项
- **create**:每次启动应用程序时,都会删除现有的表并重新创建表。
- **update**:每次启动应用程序时,都会根据实体类更新表结构(推荐用于开发环境)。
- **validate**:每次启动应用程序时,都会验证表结构是否与实体类匹配,但不会修改表。
- **none**:不执行任何DDL操作。
示例
假设您有以下实体类:
java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User extends AbstractAuditBase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and setters
}
以及包含审计字段的基类:
java
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.Instant;
@MappedSuperclass
@EntityListeners(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
@Json
======================
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Modifying
@Transactional(rollbackFor = Exception.class)
void deleteByUserName(String userName);
}
不加@Modifying不行吗?
在Spring Data JPA中,@Modifying注解用于标识一个修改数据库状态的方法,例如INSERT、UPDATE或DELETE操作。默认情况下,Spring Data JPA的查询方法(如findBy)是只读的,不会修改数据库状态。因此,对于会修改数据库状态的方法,必须使用@Modifying注解来明确指示。
为什么需要@Modifying
1. **标识修改操作**:@Modifying注解告诉Spring Data JPA,这个查询不是一个普通的SELECT查询,而是一个会修改数据库状态的操作。
2. **事务管理**:@Modifying注解通常与@Transactional一起使用,以确保修改操作在事务中执行,并在出现异常时进行回滚。
示例
以下是一个使用@Modifying和@Transactional注解的示例:
java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.transaction.annotation.Transactional;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
@Modifying
@Transactional(rollbackFor = Exception.class)
void deleteByUserName(String userName);
}
在这个示例中:
- **@Modifying**:标识deleteByUserName方法是一个修改数据库状态的方法。
- **@Transactional**:确保方法在事务中执行,并在出现异常时进行回滚。
不加@Modifying会发生什么
如果不加@Modifying注解,Spring Data JPA会将该方法视为一个普通的查询方法(即SELECT查询)。由于DELETE操作不是一个查询操作,Spring Data JPA会抛出异常,提示该方法不是一个合法的查询方法。
总结
- **必须加@Modifying**:对于任何会修改数据库状态的操作(如INSERT、UPDATE、DELETE),都必须加@Modifying注解。
- **事务管理**:通常与@Transactional一起使用,以确保操作在事务中执行,并在出现异常时进行回滚。
因此,在您的deleteByUserName方法中,@Modifying注解是必须的,以明确指示这是一个修改数据库状态的方法。
============
@JsonFormat一般用来格式化 json 数据。比如:@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
private Date date;
------
@JsonFormat这个是jpa中的吧,不是把请求参数映射到实体类吧,那这个具体干嘛的呢
没错,@JsonFormat注解与JPA无关,它主要用于控制层的返回响应格式,具体来说是控制JSON数据的序列化和反序列化。它属于Jackson库的一部分,而Jackson是Spring Boot默认的JSON处理库。
主要用途
- **控制JSON输出格式**:用于指定Java对象在序列化为JSON时的格式。
- **控制JSON输入格式**:用于指定JSON数据在反序列化为Java对象时的格式。
使用场景
1. 格式化日期和时间
在控制层返回响应时,如果需要特定的日期和时间格式,可以使用@JsonFormat:
java
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Event {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "GMT")
private Date eventDate;
// Getters and setters
}
在控制层中:
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EventController {
@GetMapping("/event")
public Event getEvent() {
Event event = new Event();
event.setEventDate(new Date());
return event;
}
}
当访问/event端点时,返回的JSON响应将包含格式化的日期:
json
{
"eventDate": "2024-08-07T02:09:33.000Z"
}
2. 格式化枚举类型
如果需要将枚举类型以字符串形式返回,可以使用@JsonFormat:
java
import com.fasterxml.jackson.annotation.JsonFormat;
public enum Status {
ACTIVE,
INACTIVE
}
public class User {
private String name;
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Status status;
// Getters and setters
}
在控制层中:
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setName("John Doe");
user.setStatus(Status.ACTIVE);
return user;
}
}
当访问/user端点时,返回的JSON响应将包含枚举的字符串表示:
json
{
"name": "John Doe",
"status": "ACTIVE"
}
==============
你的意思@JsonFormat 不能处理URL参数、路径变量或表单数据?
是的,正如你所理解的,@JsonFormat主要用于处理JSON格式的数据,而不是URL参数、路径变量或表单数据。@DateTimeFormat则专门用于处理这些非JSON格式的数据。
具体解释
@JsonFormat
- **主要用途**:控制Java对象在序列化(转换为JSON)和反序列化(从JSON转换)时的格式。
- **适用场景**:处理JSON请求体和响应体。
- **局限性**:不能直接用于解析URL参数、路径变量或表单数据。
@DateTimeFormat
- **主要用途**:格式化和解析Spring MVC中的日期和时间字段。
- **适用场景**:处理HTTP请求参数、路径变量和表单数据。
- **优势**:专门用于处理非JSON格式的数据。
示例对比
使用@JsonFormat处理JSON数据
java
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Event {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "GMT")
private Date eventDate;
// Getters and setters
}
在控制器中处理JSON请求体:
java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EventController {
@PostMapping("/event")
public void createEvent(@RequestBody Event event) {
// event.getEventDate() will be parsed according to the specified format
}
}
使用@DateTimeFormat处理URL参数
java
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
public class Event {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate eventDate;
// Getters and setters
}
在控制器中处理查询参数:
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EventController {
@GetMapping("/event")
public Event getEvent(@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
Event event = new Event();
event.setEventDate(date);
return event;
}
}
总结
- **@JsonFormat**:适用于处理JSON请求体和