Java Spring数据JPA不适用于CascaseType.PERSIST和@OneToOne
我在一个小项目中使用SpringDataJPA来列出一些信息。我有一个类LogEntry,它表示GUI上网格中的一行。其他类用于添加/显示更详细的信息,如上传/下载文件的功能。我将文件数据和元信息分为两个类/表。我的类之间的关系如下:Java Spring数据JPA不适用于CascaseType.PERSIST和@OneToOne,java,hibernate,jpa,spring-data,spring-data-jpa,Java,Hibernate,Jpa,Spring Data,Spring Data Jpa,我在一个小项目中使用SpringDataJPA来列出一些信息。我有一个类LogEntry,它表示GUI上网格中的一行。其他类用于添加/显示更详细的信息,如上传/下载文件的功能。我将文件数据和元信息分为两个类/表。我的类之间的关系如下:LogEntry->Comment->FileReference->FileData FileReference.java @Entity public class FileReference extends AbstractEntity { privat
LogEntry
->Comment
->FileReference
->FileData
FileReference.java
@Entity
public class FileReference extends AbstractEntity {
private static final long serialVersionUID = 3942449578983368585L;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
private FileData fileData;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int size;
}
@Entity
public class FileData extends AbstractEntity {
private static final long serialVersionUID = 6706563782575452010L;
@Lob
byte[] byteArray;
}
public interface LogEntryRepo<LogEntry> extends JpaRepository<LogEntry, ObjectKey> {
}
FileData.java
@Entity
public class FileReference extends AbstractEntity {
private static final long serialVersionUID = 3942449578983368585L;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
private FileData fileData;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int size;
}
@Entity
public class FileData extends AbstractEntity {
private static final long serialVersionUID = 6706563782575452010L;
@Lob
byte[] byteArray;
}
public interface LogEntryRepo<LogEntry> extends JpaRepository<LogEntry, ObjectKey> {
}
LogEntryRepo.java
@Entity
public class FileReference extends AbstractEntity {
private static final long serialVersionUID = 3942449578983368585L;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
private FileData fileData;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private int size;
}
@Entity
public class FileData extends AbstractEntity {
private static final long serialVersionUID = 6706563782575452010L;
@Lob
byte[] byteArray;
}
public interface LogEntryRepo<LogEntry> extends JpaRepository<LogEntry, ObjectKey> {
}
公共接口LogEntryRepo扩展了JpaRepository{
}
访问我的实体
@Component
@Transactional(readOnly = true)
public class LogEntryServiceImpl implements LogEntryService {
@Autowired
LogEntryRepo repo;
@Autowired
FileReferenceRepo fileReferenceRepo;
/**
* save a changed LogEntry
*/
@Override
@Secured(Roles.ROLE_WRITE)
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void saveLogEntry(LogEntry logEntry) {
if (logEntry != null) {
LogEntry one = repo.getOne(logEntry.getObjectKey());
if (one != null) {
if (one.isAudited()) {
throw new AlreadyAuditedException();
}
}
}
repo.save(logEntry);
}
@Override
@Secured(Roles.ROLE_READ_ONLY)
@Transactional(readOnly = true)
public List<LogEntry> loadAll() {
List<LogEntry> findAll = repo.findAll();
for (LogEntry logEntry : findAll) {
setNullForFileData(logEntry); // avoid serialization problems with lazy loading proxies
}
return findAll;
}
@Override
@Secured(Roles.ROLE_READ_ONLY)
@Transactional(readOnly = true)
public FileReference loadFileData(ObjectKey key) {
FileReference fileReference = fileReferenceRepo.findOne(key);
// fileData ist lazy loaded. call once to load it from db.
fileReference.getFileData().getObjectKey();
return fileReference;
}
}
@组件
@事务(只读=真)
公共类LogEntryServiceImpl实现LogEntryService{
@自动连线
LogEntryRepo回购;
@自动连线
filereferencepo filereferencepo;
/**
*保存更改的日志条目
*/
@凌驾
@安全(角色。角色\写入)
@事务性(只读=错误,传播=传播。需要\u新建)
公共无效保存日志项(日志项日志项){
if(logEntry!=null){
LogEntry one=repo.getOne(LogEntry.getObjectKey());
如果(一!=null){
如果(一个.isAudited()){
抛出新的AlreadyAuditedException();
}
}
}
回购保存(日志输入);
}
@凌驾
@安全(角色。角色只读)
@事务(只读=真)
公共列表loadAll(){
List findAll=repo.findAll();
用于(日志条目:findAll){
setNullForFileData(logEntry);//避免延迟加载代理的序列化问题
}
返回findAll;
}
@凌驾
@安全(角色。角色只读)
@事务(只读=真)
公共文件引用loadFileData(ObjectKey){
FileReference FileReference=filereferencepo.findOne(键);
//文件数据延迟加载。请调用一次以从数据库加载它。
fileReference.getFileData().getObjectKey();
返回文件引用;
}
}
通过这种分离,我可以读取所有LogEntry
s,而无需从数据库加载每个文件,但我仍然能够显示文件的元信息并提供下载方法。我在每个关系上都使用CascadeType.ALL。这非常有效,直到我尝试更改注释中已经存在的一些数据。应用程序试图保存一个FileReference
,其中对FileData
的引用为空,这是不允许的。然后我尝试将关系FileReference
->FileData
更改为CascadeType.PERSIST
,但这会导致在尝试保存注释时出现异常
org.springframework.orm.jpa.JpaObjectRetrievalFailureException:无法找到id为123的..文件数据;嵌套异常为javax.persistence.EntityNotFoundException:无法找到id为PersistentStringObjectKey 123的..文件数据
如果没有CascadeType.MERGE
,spring数据似乎无法处理这个问题。我想到的唯一两个解决方案是,我使用CascadeType.ALL
,当我必须更改和更新Comment
时,我为所有FileReferences
加载FileData
,然后保存Comment
,或者我不使用CascadeType.ALL,并制作一个DAO,在其中保存每个对象的每个引用,在存储“主”对象之前。但这一点都不好
问题:
有人知道如何使用CasecadeType
s实现我的目标的正确方法吗?不要在每次选择时加载FileData
,也不要在将更改保存到其他实体之前预加载它?因为java insertBLOB
到BD
中需要插入和更新,而不是只插入,所以cascade={CascadeType.PERSIST,CascadeType.MERGE}
是必需的。首先,Spring是不相关的。您的JPA提供程序是Hibernate,而不是Spring。其次,问题是代码,您没有发布它。要在不加载现有文件数据的情况下获取对该文件数据的引用,只需使用EntityManager.getReference()
(或JpaRepository.getOne()
)我使用spring数据jpa,因此我不直接使用jpa。我将编辑问题和代码,了解如何访问我的实体。我希望在不加载Yes的情况下获得日志条目的列表,而不是单个文件数据,因此我建议使用JpaRepository.getOne().JpaRepository是Spring-data-jpa中的一个类。也许我不理解你。加载一个文件数据不是问题。我提供了一个Web服务,我有三个用例。一个是加载一个日志项列表。对于这个用例,我不想加载所有与我加载的日志项相关的文件数据。第二个用例是,我想ange LogEntry中的值。在这种情况下,我不想加载FileData。问题是,FileData的引用为null,因为我从外部传递LogEntry(因此没有延迟加载代理)。第三种情况是,我只想加载一个FileData供用户下载。这没有问题。我更新了我的问题如何访问我的实体