Jos*_* M. 6 java mysql database orm hibernate
我们在使用 Spring Boot (v2.1.5) 和 Hibernate (5.3.10) 时遇到了一个非常奇怪的问题。我们有一个DocumentRevision带有外键的表,例如Document -> DocumentRevision -> User. 当我们 fetch 时Document,DocumentRevision是eager-fetched 和Userlazy-fetched。这在大多数情况下都能正常工作,但在非常具体的情况下以非常具体的方式失败。
DocumentRevision实际上有两个外键User:uploader (NOT NULL)和approver (NULL)。
中的列之一DocumentRevision是source VARCHAR(255) NULL。
当DocumentRevision.source包含142 个或更多字符的长度时,uploaderHibernate 不获取,并且NullPointerException结果。对于同一条记录,当该source列的null长度为0-141 个字符时,uploader正确提取。这是 100% 可重现的,142 个字符是突破点。请注意,这些是 ASCII 字符,没什么特别的(排序规则是utf8mb4_unicode_ci)。
VARCHAR未损坏记录中的所有其他字段没有任何影响 - 一切正常(实际上只是source导致此问题的列,并且仅当它包含 142 个或更多字符时)source列中插入 142 个或更多字符来打破未损坏的记录approverId到null了一个破纪录的修复问题uploaderId 不解决问题creator和approver数据库(指向同持有完全相同的值User),Hibernate是能够获取approver,但不creator-那就是,当我检查生成的Document,它DocumentRevision没有creator,但它确实有一个approver-这仅在调试和检查值时为真调试失败
我已经尝试了以下相同的结果。
source列的大小@Column注释替换为@Column(length = X)uploader为FetchType.EAGERoptional = false从uploader更新:我们注意到,这也可以通过从Documentto DocumentRevision( Document.documentRevisions) 中删除多对一关系来解决。此外,从DocumentRevisionto Document( DocumentRevision.document) 中删除一对一关系可以解决此问题。我目前的理论是某种循环引用——但同样,日志中没有任何来自 Hibernate 的内容。
以下是一些片段:
表:文件
CREATE TABLE `Document` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createdDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deletedDateTime` timestamp NULL DEFAULT NULL,
`lastModifiedDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` bigint(20) NOT NULL,
`sectionId` int(11) NOT NULL,
`currentRevisionId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_Document_sectionId_Section_id` (`sectionId`),
KEY `FK_Document_currentRevisionId_DocumentRevision_id` (`currentRevisionId`),
CONSTRAINT `FK_Document_currentRevisionId_DocumentRevision_id` FOREIGN KEY (`currentRevisionId`) REFERENCES `DocumentRevision` (`id`),
CONSTRAINT `FK_Document_sectionId_Section_id` FOREIGN KEY (`sectionId`) REFERENCES `Section` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34074 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)
表:文档修订
CREATE TABLE `DocumentRevision` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createdDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deletedDateTime` timestamp NULL DEFAULT NULL,
`lastModifiedDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` bigint(20) NOT NULL,
`contentLength` bigint(20) DEFAULT NULL,
`uploaderId` binary(16) NOT NULL,
`approverId` binary(16) DEFAULT NULL,
`fileId` int(11) NOT NULL,
`parsed` tinyint(1) NOT NULL DEFAULT '0',
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`source` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`sourcePublishDateTime` timestamp NULL DEFAULT NULL,
`description` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`coordinateSystem` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL,
`mgrs` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`latitude` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`longitude` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`location` point DEFAULT NULL,
`sourceUrl` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`marking` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
`approvedDateTime` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_DocumentRevision_fileId_Document_id` (`fileId`),
KEY `FK_DocumentRevision_uploaderId_User_id` (`uploaderId`),
KEY `FK_DocumentRevision_approverId_User_id` (`approverId`),
CONSTRAINT `FK_DocumentRevision_approverId_User_id` FOREIGN KEY (`approverId`) REFERENCES `User` (`id`),
CONSTRAINT `FK_DocumentRevision_fileId_Document_id` FOREIGN KEY (`fileId`) REFERENCES `Document` (`id`),
CONSTRAINT `FK_DocumentRevision_uploaderId_User_id` FOREIGN KEY (`uploaderId`) REFERENCES `User` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34080 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)
表:用户
CREATE TABLE `User` (
`id` binary(16) NOT NULL,
`createdDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deletedDateTime` timestamp NULL DEFAULT NULL,
`lastModifiedDateTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`version` bigint(20) NOT NULL,
`active` tinyint(1) NOT NULL,
`firstName` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`lastName` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(60) COLLATE utf8mb4_unicode_ci NOT NULL,
`primaryEmailAddress` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`hidden` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `IDX_Person_username` (`username`),
KEY `IDX_Person_primaryEmailAddress` (`primaryEmailAddress`),
KEY `IDX_Person_lastName` (`lastName`),
KEY `IDX_Person_firstName` (`firstName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Run Code Online (Sandbox Code Playgroud)
映射:文档
@Entity
@Table(name = "Document")
@Data
@EqualsAndHashCode(callSuper = true)
public class Document extends BaseEntity<Integer>
{
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "sectionId")
private Section section;
@ManyToOne(cascade = CascadeType.REMOVE)
@JoinColumn(name = "currentRevisionId")
private DocumentRevision currentRevision;
@OneToMany(mappedBy = "document", cascade = CascadeType.REMOVE)
private List<DocumentRevision> documentRevisions = new ArrayList<>();
}
Run Code Online (Sandbox Code Playgroud)
映射:文档修订
@Entity
@Table(name = "DocumentRevision")
@Data
@EqualsAndHashCode(callSuper = true)
public class DocumentRevision extends BaseEntity<Integer>
{
@JsonIgnore
@ManyToOne
@JoinColumn(name = "documentId", nullable = false)
private Document document;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "uploaderId")
private User uploader;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "approverId")
private User approver;
@Column
private String source;
// other fields removed for brevity
}
Run Code Online (Sandbox Code Playgroud)
映射:用户
@EqualsAndHashCode
@Data
@Entity
@Table(name = "User")
public class User extends BaseEntity<UUID>
{
@Column(length = 60, nullable = false)
private String username;
@Column(length = 25, nullable = false)
private String firstName;
@Column(length = 25, nullable = false)
private String lastName;
@Column(length = 255)
private String primaryEmailAddress;
@Column(nullable = false)
private boolean active;
@Column(nullable = false)
private boolean hidden;
}
Run Code Online (Sandbox Code Playgroud)
我意识到这是很多信息,但这让我难住了!