spring - 为什么惰性初始化实体在事务边界内分配给 DTO 时为空?
问题描述
我正在将我的遗留代码移植到 spring boot + angular 应用程序,这是之前的 spring + angular js。我的 pojo 中有 @ManyToOne 关系,这是懒惰的。当我尝试从原始对象创建 DTO 对象时,原始对象内的子对象在发送到客户端时为空。在我的旧应用程序中,相同的代码可以完美运行。
如果我让它急切,或者在创建 DTO 之前在该子元素上调用 sysout,那么它可以工作,可能是因为子对象的 getter 在内部被调用。
父对象
public class ComponentInfo implements Serializable{
private static final long serialVersionUID = -1135710750835719391L;
@Id
@Column(name="TAGGING_KEY")
private String taggingKey;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "COMPONENT_CONFIG_ID",nullable = true)
private ReportComponentConfig componentConfig;
@Column(name = "DESCRIPTION", length = 200)
private String description;
@Column(name = "ORIGINAL_FILE_NAME",length=50)
private String originalFileName;
@Column(name = "OVERRIDE_DOCUMENT")
private Boolean overrideDocument = false;
@Column(name = "START_DATE")
private Date startDate;
@OneToMany(mappedBy="componentInfo",cascade=CascadeType.ALL,orphanRemoval=true)
private List<TransactionComponentInfo> transactionComponentInfo = new ArrayList<>(0);
@Column(name = "SHOW_GLOBAL")
private Boolean showGlobal = false;
}
子对象
public class ReportComponentConfig {
@Id
@TableGenerator(name = "COMPONENT_CONFIG_ID", table = "ID_GENERATOR", pkColumnName = "GEN_KEY", valueColumnName = "GEN_VALUE", pkColumnValue = "COMPONENT_CONFIG_ID", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "COMPONENT_CONFIG_ID")
@Column(name = "COMPONENT_CONFIG_ID", unique = true, nullable = false)
private int id;
@Column(name = "NAME", nullable = false, length = 200)
private String name;
@Column(name = "TAG", nullable = false, length = 200)
private String tag;
@Column(name = "COMP_CONFIG", nullable = false, columnDefinition = "VARCHAR(MAX)")
private String config;
@Column(name = "PUBLISHED_CONFIG", columnDefinition = "VARCHAR(MAX)")
private String publishedConfig;
@Column(name = "IS_PUBLISHED")
private boolean published = false;
@ManyToOne
@JoinColumn(name = "COMPONENT_ID", nullable = false)
private Component component;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "CONFIG_REPORT_MAPPING", joinColumns = @JoinColumn(name = "COMPONENT_CONFIG_ID"))
@Column(name = "REPORT_ID")
private Set<Long> reports = new HashSet<>(0);
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "CONFIG_TRANSACTION_MAPPING", joinColumns = @JoinColumn(name = "COMPONENT_CONFIG_ID"))
@Column(name = "TRANSACTION_ID")
private Set<Long> transactions = new HashSet<>(0);
@Column(name = "VIEWS", columnDefinition = "VARCHAR(MAX)")
private String views;
}
DTO
public class ComponentInfoDTO implements Cloneable {
private ReportComponentConfig componentConfig;
private String taggingKey;
private String description;
private String originalFileName;
private Date startDate;
private Boolean overrideDocument;
private Boolean showGlobal;
private ComponentInfoDTO parentComponentInfo;
public ComponentInfoDTO() {
}
public ComponentInfoDTO(ComponentInfo ci, TransactionComponentInfo transactionComponentInfo) {
this.componentConfig = ci.getComponentConfig();//this is object is null
this.taggingKey = ci.getTaggingKey();
this.description = ci.getDescription();
this.originalFileName = ci.getOriginalFileName();
this.startDate = ci.getStartDate();
this.overrideDocument = ci.getOverrideDocument();
this.showGlobal = ci.getShowGlobal();
if (transactionComponentInfo != null) {
this.parentComponentInfo = this.clone();
this.startDate = transactionComponentInfo.getStartDate();
this.overrideDocument = transactionComponentInfo.getOverrideDocument();
this.description = transactionComponentInfo.getDescription();
this.originalFileName = transactionComponentInfo.getOriginalFileName();
this.showGlobal = true;
}
}
}
编辑:两者都是相同的,但在旧情况下,我在客户端获取子对象,在新情况下,我得到空值。
这是我在使用惰性初始化的旧应用程序中获取的数据
[ {
"componentConfig" : {
"id" : 3,
"name" : "Monthly Origination By Region",
"tag" : "CHART_monthlyOriginationByRegion",
"config" : "xyz",
"published" : true,
"component" : {
"id" : "CHART",
"name" : "Chart",
"defaultConfig" : null,
"htmlTag" : "<chart></chart>",
"filePath" : "chart/chart.component.js",
"dependentScriptsSrc" : [ ],
"dependencies" : null
},
"reports" : [ 3 ],
"transactions" : [ 2 ],
"views" : "{\"monthlyOriginationByRegion\": {\"key\": \"MONTHLY_ORIGINATION_BY_REGION\"}}"
},
"taggingKey" : "3",
"description" : "asdfasd\nasdfadf",
"originalFileName" : "Citi Tape - 2141 - GEBL0501 - 2019 Oct 04.xlsm",
"startDate" : "2019-10-28T18:30:00.000+0000",
"overrideDocument" : true,
"showGlobal" : true,
"parentComponentInfo" : null
} ]
这是新应用程序中的数据
[ {
"componentConfig" : null,
"taggingKey" : "3",
"description" : "asdfasd\nasdfadf",
"originalFileName" : "Citi Tape - 2141 - GEBL0501 - 2019 Oct 04.xlsm",
"startDate" : "2019-10-28T18:30:00.000+0000",
"overrideDocument" : true,
"showGlobal" : true,
"parentComponentInfo" : null
} ]
组件配置不应该为空,如果让它渴望获取它可以在新应用程序中工作,但在我的旧应用程序中,它正在使用延迟获取。
解决方案
您在调试器窗口中看到的是 HibernateProxy。该代理的字段从未初始化!getter 被拦截并委托给位于代理内部某处的加载实体。
这意味着您永远不能直接调用字段,因为您不知道对象是代理还是加载的实体。你总是需要在吸气剂上工作。
您如何将实体映射到 DTO?很可能您使用字段访问,而您应该使用 getter。
推荐阅读
- python - 帮助在准确的时间运行几行代码
- pandas - 熊猫将值从一列复制到另一列,如果一个存在而另一个是 NaN/Null
- javascript - 反应:未捕获的类型错误:无法读取 mapStateToProps 中未定义的属性“名称”
- c# - 如何解决此“未指定身份验证方案,并且未找到 DefaultChallengeScheme”
- python - 无法在带有 Errno 97 的 AWS lambda 上使用 wget.download 下载文件
- mysql - 如何避免在这个 sql 中使用 mysql 函数?
- typescript - 如何避免`后续的属性声明必须具有与接口相同的类型`
- laravel - 使用 Livewire 和 Filepond 编辑模型时处理预先存在的图像
- c++ - 为什么括号中的 AND 与没有的评估不同?
- jquery - jQuery 选择器,只要它没有特定的类,就可以按类及其子元素对元素的点击进行定位