java - Hibernate/SQL SERVER java.sql.SQLException:无法将 NULL 值插入具有默认值的列中
问题描述
拥有 Hibernate 和 SQL Server,当我想更新一个名为Client的实体时遇到了问题。客户端实体具有审计字段,例如创建/更新后者的当前用户的名称以及这些操作发生的日期时间。这里的问题是,当更新发生时,会抛出java.sql.SQLException,在日志下方以获取更多详细信息:
原因:java.sql.SQLException:无法将值 NULL 插入列 'IS_SUPPRIMER',表 'IJSS_DB.dbo.TF_CODE_PAIE';列不允许空值。更新失败。在 net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:368) ~[jtds-1.2.4.jar:1.2.4] 在 net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:第2820章~[jtds-1.2.4.jar:1.2.4]在net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2258)~[jtds-1.2.4.jar:1.2.4]在net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:632) ~[jtds-1.2.4.jar:1.2.4] 在 net.sourceforge.jtds.jdbc.JtdsStatement.processResults(JtdsStatement.java:584 ) ~[jtds-1.2.4.jar:1.2.4] at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQL(JtdsStatement.java:546) ~[jtds-1.2.4.jar:1.2.4] at net .sourceforge.jtds.jdbc。
请注意,IS_SUPPRIMER列是指示该列是否被删除的审计字段(IS_SUPPRIMER=true /deleted,IS_SUPPRIMER=false/未删除),因此我们进行了逻辑删除而不是物理删除。
这里奇怪的是,映射到TF_CLIENT表的客户端实体在脚本下方有一个名为IS_SUPPRIMER的默认值列:
CREATE TABLE [dbo].[TF_CLIENT] (
[CLT_ID] [bigint] IDENTITY(1,1) NOT NULL,
[CLT_LIBELLE] [nvarchar](50) NOT NULL,
[CLT_DT_OUVERTURE] [date] NOT NULL,
[CLT_DT_FERMETURE] [date],
[CLT_B_ACTIF] [bit] CONSTRAINT DF_TF_CLIENT_B_ACTIF DEFAULT 1 NOT NULL,
[DATE_CREATION] [datetime2](3),
[DATE_MODIFICATION] [datetime2](3),
[DATE_SUPRESSION] [datetime2](3),
[AUTEUR_CREATION] [nvarchar](100),
[AUTEUR_MODIFICATION] [nvarchar](100),
[AUTEUR_SUPRESSION] [nvarchar](100),
[IS_SUPPRIMER] [bit] CONSTRAINT DF_TF_UTILISATEUR_IS_SUPPRIMER DEFAULT 0 NOT NULL
CONSTRAINT UQ_CLT_LIBELLE UNIQUE(CLT_LIBELLE),
CONSTRAINT PK_TF_CLIENT PRIMARY KEY CLUSTERED
(
[CLT_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
END
那么为什么更新这样的客户端实体需要一个已经有默认值的IS_SUPPRIMER值呢?
在 Auditing 类的代码下面:
@Embeddable
@Getter
@Setter
@NoArgsConstructor
public class FieldAuditing implements Serializable {
@Column(name = "DATE_CREATION")
private Instant createdAt;
@Column(name = "DATE_MODIFICATION")
private Instant updatedAt;
@Column(name = "DATE_SUPRESSION")
private Instant deletedAt;
@Column(name = "AUTEUR_CREATION", length = 100)
private String createdBy;
@Column(name = "AUTEUR_MODIFICATION", length = 100)
private String updatedBy;
@Column(name = "AUTEUR_SUPRESSION", length = 100)
private String deletedBy;
@Column(name = "IS_SUPPRIMER")
private Boolean isDeleted = false;
@PrePersist
public void prePersist() {
if (this.isDeleted == null)
setIsDeleted(Boolean.FALSE);
setCreatedAt(Instant.now());
setCreatedBy(LoggedInUser.get());
}
@PreUpdate
public void preUpdate() {
if (this.isDeleted == null)
setIsDeleted(Boolean.FALSE);
setUpdatedAt(Instant.now());
setUpdatedBy(LoggedInUser.get());
}
}
还有客户端实体类:
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Table(name="TF_CLIENT", schema="dbo")
public class Client implements Serializable {
private static final long serialVersionUID = 8832848102370267801L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator="native")
@GenericGenerator(name = "native", strategy = "native")
@Column(name = "CLT_ID", nullable = false)
private Long id;
@Column(name = "CLT_LIBELLE", nullable = false, length = 50, unique = true)
private String libelle;
@Temporal(TemporalType.DATE)
@Column(name = "CLT_DT_OUVERTURE", nullable = false)
private Date dateOuverture;
@Temporal(TemporalType.DATE)
@Column(name = "CLT_DT_FERMETURE")
private Date dateFermeture;
@Column(name = "CLT_B_ACTIF")
private boolean isActif;
@Embedded
private FieldAuditing fieldAuditing = new FieldAuditing() ;
@JsonIgnore
@OneToMany
@Builder.Default
@JoinColumn(name="CLI_ID")
Set<Etablissement> etablissements = Collections.emptySet();
@JsonIgnore
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
@JoinColumn(name="CLI_ID")
@Builder.Default
@Setter(AccessLevel.NONE)
Set<CodePaie> codePaies = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, mappedBy = "client", orphanRemoval = true)
@Builder.Default
@Setter(AccessLevel.NONE)
Set<ClientAction> clientActions = Sets.newHashSet();
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, mappedBy = "client", orphanRemoval = true)
@Builder.Default
@Setter(AccessLevel.NONE)
Set<ClientEtat> clientEtats = Sets.newHashSet();
public void addClientActions(Set<ClientAction> clientActions) {
clientActions.forEach(clientAction -> clientAction.setClient(this));
this.clientActions.addAll(clientActions);
}
public void addClientEtats(Set<ClientEtat> clientEtats) {
clientEtats.forEach(etat -> etat.setClient(this));
this.clientEtats.addAll(clientEtats);
}
public void addCodePaies(Set<CodePaie> codePaies) {
codePaies.forEach(codePaie -> codePaie.setClient(this));
this.codePaies.addAll(codePaies);
}
public void removeAllCodePaie() {
codePaies.forEach(codePaie -> codePaie.setClient(null));
this.codePaies.clear();
}
}
最后是更新客户端实体的代码片段:
private ClientDto save(ClientDto clientDto, Client client) {
startDateShouldBeBeforeEndDate(clientDto);
hasUniqueCodePaies(clientDto.getCodePaies());
Client clientSaved = clientRepository.save(clientMapper.toEntity(clientDto, client));
clientMapper.addOrRemoveClientActions(clientDto, clientSaved);
clientMapper.addOrRemoveClientEtats(clientDto, clientSaved);
clientRepository.save(clientSaved);
clientDto.setId(clientSaved.getId());
return clientDto;
}
请您看看我的问题描述并帮助我。
我很感激任何帮助。
解决方案
异常表明问题出在 TF_CODE_PAIE 而不是 TF_CLIENT。我猜 TF_CODE_PAIE.IS_SUPPRIMER 没有默认值。
推荐阅读
- bash - 在 fasta 序列名称中添加标签
- c# - 如何在 WPF 中导航页面和 MainWindow?是否可以使用页面而不是多个窗口?
- c++ - reinterpret_cast 到相同的类型
- ios - CollectionView 约束打破
- python - 如何从新文章中提取犯罪人的姓名?
- c++ - 调用 log4c_fini() 后 log4c rollingpolicy append="true" 没有发生
- c# - 如何在 OnActionExecuted 中获取“ViewData [“x”]”值?
- .net - 在 Telerik Winforms .Net Core 中加载 RadForm 之前出错
- c# - c# 7.1 中的通用模式匹配是什么?
- r - 为什么点在 base::split(.$cyl) 中?