Spring 弹簧座&x2B;JPA&x2B;H2@manytone双向关系。无法存储子实体
我在尝试使用JPA存储某些实体时遇到一些问题,情况如下:Spring 弹簧座&x2B;JPA&x2B;H2@manytone双向关系。无法存储子实体,spring,jpa,one-to-many,h2,many-to-one,Spring,Jpa,One To Many,H2,Many To One,我在尝试使用JPA存储某些实体时遇到一些问题,情况如下: WebMessageEntity.java @EqualsAndHashCode @Data @Entity(name = "web_message") @NoArgsConstructor @AllArgsConstructor @Builder public class WebMessageEntity{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter
@EqualsAndHashCode
@Data
@Entity(name = "web_message")
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class WebMessageEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
@Column(name = "WEB_MESSAGE_ID")
private Long id;
@Getter
@Setter
@NotEmpty
private String hotelTicker;
@Getter
@Setter
@NotNull
@Enumerated(EnumType.STRING)
private WebMessageColor color;
@Getter
@Setter
@NotNull
@Enumerated(EnumType.STRING)
@Column(name = "message_type")
private WebMessageType type;
@Getter
@Setter
@NotNull
@Enumerated(EnumType.STRING)
private ReservationStep step;
@Getter
@Setter
@NotNull
@Enumerated(EnumType.STRING)
@Column(name = "message_trigger")
private WebMessageTrigger trigger;
private int duration;
@Enumerated(EnumType.STRING)
private WebMessagePosition position;
@Getter
@Setter
@NotNull
private boolean isActive;
@Getter
@Setter
@NotNull
@Convert(converter = LocalDateAttributeConverter.class)
private LocalDate startDate;
@Getter
@Setter
@NotNull
@Convert(converter = LocalDateAttributeConverter.class)
private LocalDate endDate;
@Setter
@OneToMany(mappedBy = "webMessage", cascade = CascadeType.ALL)
@NotNull
private List<WebMessageTranslationEntity> translations;
@Getter
@Setter
@NotEmpty
private String userName;
@Getter
@Setter
@NotNull
@Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime creationDate;
@Getter
@Setter
private String modifiedBy;
@Getter
@Setter
@Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime modificationDate;
//constructors
//------------------------------------------------------------------------------------------------------
private WebMessageEntity(String hotelTicker, WebMessageColor color, WebMessageType type, ReservationStep step,
WebMessageTrigger trigger, boolean isActive, LocalDate startDate, LocalDate endDate,
List<WebMessageTranslationEntity> translations, String userName, LocalDateTime creationDate,
String modifiedBy, LocalDateTime modificationDate)
{
this.hotelTicker = hotelTicker;
this.color = color;
this.type = type;
this.step = step;
this.trigger = trigger;
this.isActive = isActive;
this.startDate = startDate;
this.endDate = endDate;
this.translations = translations;
this.userName = userName;
this.creationDate = creationDate;
this.modifiedBy = modifiedBy;
this.modificationDate = modificationDate;
}
private WebMessageEntity(String hotelTicker, WebMessageColor color, WebMessageType type, ReservationStep step,
WebMessageTrigger trigger, int duration, WebMessagePosition position, boolean isActive,
LocalDate startDate, LocalDate endDate, List<WebMessageTranslationEntity> translations,
String userName, LocalDateTime creationDate, String modifiedBy, LocalDateTime modificationDate)
{
this.hotelTicker = hotelTicker;
this.color = color;
this.type = type;
this.step = step;
this.trigger = trigger;
this.setDuration(duration);
this.setPosition(position);
this.isActive = isActive;
this.startDate = startDate;
this.endDate = endDate;
this.translations = translations;
this.userName = userName;
this.creationDate = creationDate;
this.modifiedBy = modifiedBy;
this.modificationDate = modificationDate;
}
//GETTERS, SETTERS and some private field verification methods
@RestController
@RequestMapping("/api")
public class WebMessageResource {
private final WebMessageService messageService;
@Autowired
public WebMessageResource(WebMessageService messageService) {
this.messageService = messageService;
}
@PostMapping("{hotelTicker}/messages")
public ResponseEntity<?> createMessage(@RequestBody @Valid WebMessageDTO dto, @PathVariable @NotNull String hotelTicker) {
if (verifyHotelTicker(dto, hotelTicker)) {
WebMessageEntity newEntity = messageService.store(fromWebMessageDTOToEntity(dto));
HttpHeaders headers = new HttpHeaders();
//TODO rebuild URI with exact path to access resource
headers.setLocation(ControllerLinkBuilder.linkTo(FilterEntity.class).slash(newEntity.getHotelTicker()).slash(newEntity.getId()).toUri());
return new ResponseEntity<>(fromWebMessageEntityToDTO(newEntity), headers, HttpStatus.CREATED);
}
return new ResponseEntity<>("Hotel ticker specified in URI doesn't match with DTO's hotel ticker", HttpStatus.BAD_REQUEST);
}
private boolean verifyHotelTicker(WebMessageDTO dto, String hotelTicker) {
return hotelTicker.equals(dto.getHotelTicker());
}
private List<WebMessageTranslationEntity> fromTranslationDTOsToEntities(List<WebMessageTranslationDTO> translationDTOs) {
return translationDTOs
.stream()
.map(translation -> WebMessageTranslationEntity
.builder()
.content(translation.getContent())
.locale(translation.getLocale())
.build())
.collect(toList());
}
private WebMessageEntity fromWebMessageDTOToEntity(WebMessageDTO webMessageDTOs) {
return WebMessageEntity
.builder()
.hotelTicker(webMessageDTOs.getHotelTicker())
.color(webMessageDTOs.getColor())
.type(webMessageDTOs.getType())
.step(webMessageDTOs.getStep())
.trigger(webMessageDTOs.getTrigger())
.duration(webMessageDTOs.getDuration())
.position(webMessageDTOs.getPosition())
.isActive(webMessageDTOs.getIsActive())
.startDate(webMessageDTOs.getStartDate())
.endDate(webMessageDTOs.getEndDate())
.userName(webMessageDTOs.getUserName())
.creationDate(webMessageDTOs.getCreationDate())
.modifiedBy(webMessageDTOs.getModifiedBy())
.modificationDate(webMessageDTOs.getModificationDate())
.translations(this.fromTranslationDTOsToEntities(webMessageDTOs.getTransla tions()))
.build();
}
private WebMessageDTO fromWebMessageEntityToDTO(WebMessageEntity webMessageEntity) {
return WebMessageDTO
.builder()
.id(webMessageEntity.getId())
.hotelTicker(webMessageEntity.getHotelTicker())
.color(webMessageEntity.getColor())
.type(webMessageEntity.getType())
.step(webMessageEntity.getStep())
.trigger(webMessageEntity.getTrigger())
.duration(webMessageEntity.getDuration())
.position(webMessageEntity.getPosition())
.isActive(webMessageEntity.isActive())
.startDate(webMessageEntity.getStartDate())
.endDate(webMessageEntity.getEndDate())
.userName(webMessageEntity.getUserName())
.creationDate(webMessageEntity.getCreationDate())
.modifiedBy(webMessageEntity.getModifiedBy())
.modificationDate(webMessageEntity.getModificationDate())
.translations(this.fromTranslationEntitiesToDTO(webMessageEntity.getTranslations()))
.build();
}
private List<WebMessageTranslationDTO> fromTranslationEntitiesToDTO(List<WebMessageTranslationEntity> translationEntities) {
return translationEntities
.stream()
.map(translation -> WebMessageTranslationDTO
.builder()
//.id(translation.getId())
.content(translation.getContent())
.locale(translation.getLocale())
.build())
.collect(toList());
}
}
- changeSet:
id: '008-1'
author: arnau
comment: 'create table web_message'
preConditions:
- onFail: MARK_RAN
- onFailMessage: 'Table already exists, must be production environment...'
- not:
- tableExists:
tableName: web_message
changes:
- createTable:
tableName: web_message
columns:
- column:
name: WEB_MESSAGE_ID
type: NUMBER
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: hotel_ticker
type: VARCHAR(155)
constraints:
nullable: false
- column:
name: color
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_type
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: step
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_trigger
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: duration
type: NUMBER
constraints:
nullable: true
- column:
name: position
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: is_active
type: BOOLEAN(1)
constraints:
nullable: false
- column:
name: start_date
type: DATE
constraints:
nullable: false
- column:
name: end_date
type: DATE
constraints:
nullable: false
- column:
name: user_name
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: creation_date
type: TIMESTAMP
constraints:
nullable: false
- column:
name: modified_by
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: modification_date
type: TIMESTAMP
constraints:
nullable: true
- changeSet:
id: '008-2'
author: arnau
comment: 'create table web_message_translation'
preConditions:
- onFail: MARK_RAN
- onFailMessage: 'Table already exists, must be production environment...'
- not:
- tableExists:
tableName: web_message_translation
changes:
- createTable:
tableName: web_message_translation
columns:
- column:
name: MESSAGE_TRANSLATION_ID
type: NUMBER
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: locale
type: VARCHAR(2)
constraints:
unique: true
nullable: false
- column:
name: content
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_id
type: CHAR(22)
constraints:
nullable: false
references: web_message(WEB_MESSAGE_ID)
foreignKeyName: fk_web_message_translation__web_message
- changeSet:
id: '008-3'
author: arnau
comment: 'Add unique constraint by locale and web_message_id'
changes:
- addUniqueconstraint:
tableName: web_message_translation
columnNames: locale, message_id
constraintName: uc_locale__message_id
webMessageTranslationEntity
存储在message\u id
列中,该列的值为NULL,显然,DB会拒绝该项,因为该字段被设置为非NULL
如何使用此必备项存储这些实体?@NotNull
@NotNull
private List<WebMessageTranslationEntity> translations;
私人名单翻译;
这会将外键上的约束设置为不为null
@NotNull
私人名单翻译;
这会将外键上的约束设置为不为null
更改约束不是一个选项,因此我尝试在WebMessageTranslation entit的WebMessageEntity属性中添加一个CascadeType.ALL,如下所示:
@manytone(cascade=CascadeType.ALL)@JoinColumn(name=“MESSAGE\u ID”)私有WebMessageEntity webMessage代码>但是问题仍然存在,所以最后我必须单独持久化这些实体,首先持久化WebMessageEntity,然后持久化WebMessageTranslations。但我认为这必须是一种更干净的方式。我认为它已经解决了,但如果我找到更好的方法,我会把它贴在这里。Thank's a lot@amerqarabsat级联定义了子实体上操作的级联,因此如果您有一个对B的引用的a,并且您希望在持久化a时持久化引用的B,那么您将在a内部根据B引用的关系定义级联,因此,在您的cane中,它应该在OneToMany而不是ManyTone中更改约束不是一个选项,因此我尝试添加一个CascadeType.ALL在WebMessageTranslation entit的WebMessageEntity属性中,如下所示:@manyTone(cascade=CascadeType.ALL)@JoinColumn(name=“MESSAGE\u ID”)私有WebMessageEntity webMessage代码>但是问题仍然存在,所以最后我必须单独持久化这些实体,首先持久化WebMessageEntity,然后持久化WebMessageTranslations。但我认为这必须是一种更干净的方式。我认为它已经解决了,但如果我找到更好的方法,我会把它贴在这里。Thank's a lot@amerqarabsat级联定义了子实体上操作的级联,因此,如果您有一个对B的引用的a,并且您希望在持久化a时持久化引用的B,那么您将在a内部根据B引用的关系定义级联,因此在您的cane中,它应该在一个域中,而不是在多个域中
- changeSet:
id: '008-1'
author: arnau
comment: 'create table web_message'
preConditions:
- onFail: MARK_RAN
- onFailMessage: 'Table already exists, must be production environment...'
- not:
- tableExists:
tableName: web_message
changes:
- createTable:
tableName: web_message
columns:
- column:
name: WEB_MESSAGE_ID
type: NUMBER
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: hotel_ticker
type: VARCHAR(155)
constraints:
nullable: false
- column:
name: color
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_type
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: step
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_trigger
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: duration
type: NUMBER
constraints:
nullable: true
- column:
name: position
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: is_active
type: BOOLEAN(1)
constraints:
nullable: false
- column:
name: start_date
type: DATE
constraints:
nullable: false
- column:
name: end_date
type: DATE
constraints:
nullable: false
- column:
name: user_name
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: creation_date
type: TIMESTAMP
constraints:
nullable: false
- column:
name: modified_by
type: VARCHAR(255)
constraints:
nullable: true
- column:
name: modification_date
type: TIMESTAMP
constraints:
nullable: true
- changeSet:
id: '008-2'
author: arnau
comment: 'create table web_message_translation'
preConditions:
- onFail: MARK_RAN
- onFailMessage: 'Table already exists, must be production environment...'
- not:
- tableExists:
tableName: web_message_translation
changes:
- createTable:
tableName: web_message_translation
columns:
- column:
name: MESSAGE_TRANSLATION_ID
type: NUMBER
autoIncrement: true
constraints:
primaryKey: true
nullable: false
- column:
name: locale
type: VARCHAR(2)
constraints:
unique: true
nullable: false
- column:
name: content
type: VARCHAR(255)
constraints:
nullable: false
- column:
name: message_id
type: CHAR(22)
constraints:
nullable: false
references: web_message(WEB_MESSAGE_ID)
foreignKeyName: fk_web_message_translation__web_message
- changeSet:
id: '008-3'
author: arnau
comment: 'Add unique constraint by locale and web_message_id'
changes:
- addUniqueconstraint:
tableName: web_message_translation
columnNames: locale, message_id
constraintName: uc_locale__message_id
@NotNull
private List<WebMessageTranslationEntity> translations;