首页 > 解决方案 > 为什么惰性初始化实体在事务边界内分配给 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
} ]

组件配置不应该为空,如果让它渴望获取它可以在新应用程序中工作,但在我的旧应用程序中,它正在使用延迟获取。

标签: springhibernatejpa

解决方案


您在调试器窗口中看到的是 HibernateProxy。该代理的字段从未初始化!getter 被拦截并委托给位于代理内部某处的加载实体。

这意味着您永远不能直接调用字段,因为您不知道对象是代理还是加载的实体。你总是需要在吸气剂上工作。

您如何将实体映射到 DTO?很可能您使用字段访问,而您应该使用 getter。


推荐阅读