JPA保存将嵌入式对象转换为代理

时间:2018-08-23 12:17:29

标签: java hibernate jpa

我正在尝试使用JPA映射旧数据库,因此无法更改数据库结构。我有一个实体TableA和一个oneToMany集合tableBs,其中外键在TableB上。此集合中的对象与TableC具有ManyToOne关系。 TableB与TableD也具有oneToOne关系,外键也位于tableB上。 TableD与TableE(具有外键的tableD)具有ManyToOne关系,最后TableE与tableC(具有外键的tableE)具有oneToOne。

当我在表A上调用保存时,我希望它级联对收集表B的所有更改,因此我将级联保存操作。这似乎工作正常,但是当我向集合中添加一个新的TableB实体时,我在tableB实体上设置了一个tableC对象,但是保存后成为代理,但是我需要对其进行初始化。我在下面模拟了一个例子。

数据库表...

CREATE TABLE TABLEA (tableAPk VARCHAR2(10) PRIMARY KEY);
CREATE TABLE TABLEC (ID NUMBER PRIMARY KEY);
CREATE TABLE TABLEE (ID NUMBER PRIMARY KEY, tableEProperty NUMBER,  
CONSTRAINT FK_TABLEC2 FOREIGN KEY (tableEProperty) REFERENCES TABLEC(ID));
CREATE TABLE TABLED (ID NUMBER PRIMARY KEY, tableDProperty NUMBER, 
CONSTRAINT FK_TABLEE FOREIGN KEY (tableDProperty) REFERENCES TABLEE(ID));
CREATE TABLE TABLEB (ID NUMBER PRIMARY KEY, tableBProperty VARCHAR2(10), tableBProperty2 NUMBER, tableBProperty3 NUMBER,
CONSTRAINT FK_TABLEA FOREIGN KEY (tableBProperty) REFERENCES TABLEA (tableAPk), 
CONSTRAINT FK_TABLEC FOREIGN KEY (tableBProperty2) REFERENCES TABLEC (ID),
CONSTRAINT FK_TABLED FOREIGN KEY (tableBProperty3) REFERENCES TABLED (ID));
CREATE SEQUENCE TABLEA_SEQ START WITH 1;
CREATE SEQUENCE TABLEB_SEQ START WITH 1;
CREATE SEQUENCE TABLEC_SEQ START WITH 1;
CREATE SEQUENCE TABLED_SEQ START WITH 1;
CREATE SEQUENCE TABLEE_SEQ START WITH 1;

Java代码:

@Entity
public class TableA {

    @Id
    private String tableAPk;

    @OneToMany(mappedBy="tableBProperty", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true) 
    private List<TableB> tableBs = new ArrayList<TableB>();

    public String getTableAPk() {
        return tableAPk;
    }

    public void setTableAPk(String tableAPk) {
        this.tableAPk = tableAPk;
    }

    public List<TableB> getTableBs() {
        return tableBs;
    }

    public void setTableBs(List<TableB> tableBs) {
        this.tableBs = tableBs;
    }

}

@Entity
public class TableB {

    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLEB_SEQ")
    @SequenceGenerator(sequenceName = "TABLEB_SEQ", allocationSize = 1, name = "TABLEB_SEQ")
    private Integer id;

    private String tableBProperty;

    @ManyToOne
    @JoinColumn(name = "tableBProperty2")
    private TableC tableC;

    @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name="tableBProperty3")
    private TableD tableD;

    public TableB() {} 

    public TableB(String tableBProperty, TableC tableC, TableD tableD) {
        this.tableBProperty = tableBProperty;
        this.tableC = tableC;
        this.tableD = tableD;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTableBProperty() {
        return tableBProperty;
    }

    public void setTableBProperty(String tableBProperty) {
        this.tableBProperty = tableBProperty;
    }

    public TableC getTableC() {
        return tableC;
    }

    public void setTableC(TableC tableC) {
        this.tableC = tableC;
    }

    public TableD getTableD() {
        return tableD;
    }

    public void setTableD(TableD tableD) {
        this.tableD = tableD;
    }

}

@Entity
public class TableC {

    @Id private Integer id;

    @OneToOne(mappedBy="tableC")
    private TableE tableE;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public TableE getTableE() {
        return tableE;
    }

    public void setTableE(TableE tableE) {
        this.tableE = tableE;
    }
}

@Entity
public class TableD {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLED_SEQ")
    @SequenceGenerator(sequenceName = "TABLED_SEQ", allocationSize = 1, name = "TABLED_SEQ")
    private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST})
    @JoinColumn(name="tableDProperty")
    private TableE tableE;

    public TableD() {}

    public TableD(TableE tableE) {
        this.tableE = tableE;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }


}

@Entity
public class TableE {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLEE_SEQ")
    @SequenceGenerator(sequenceName = "TABLEE_SEQ", allocationSize = 1, name = "TABLEE_SEQ")
    private Integer id;

    @OneToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "tableEProperty")
    private TableC tableC;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public TableC getTableC() {
        return tableC;
    }

    public void setTableC(TableC tableC) {
        this.tableC = tableC;
    }

}

public interface TableARepository extends JpaRepository<TableA, String>{
}

public interface TableCRepository extends JpaRepository<TableC, Integer> {
}

@RunWith(SpringRunner.class)
@SpringBootTest
public class TableARepositoryTest {

    private static final Integer TEST_ID = -1;
    private static final String TEST_ID_STRING = "TEST1";

    @Autowired protected DataSource ds;
    @Autowired private TableARepository repoA;
    @Autowired private TableCRepository repoC;
    protected JdbcTemplate jdbcTemplate;

    @Before
    public void setUp() throws Exception{
        jdbcTemplate = new JdbcTemplate(ds);
        String insertASql = "insert into TableA (tableAPk) values (?)";
        jdbcTemplate.update(insertASql, new Object[]{TEST_ID_STRING});
        String insertCSql = "insert into TableC (id) values (?)";
        jdbcTemplate.update(insertCSql, new Object[]{TEST_ID});
        String insertESql = "insert into TableE (id, tableEProperty) values (?, ?)";
        jdbcTemplate.update(insertESql, new Object[]{TEST_ID, TEST_ID});
    }

    @After
    public void tearDown() throws Exception{
        String deleteBSql = "delete from TableB where tableBProperty = ?";
        jdbcTemplate.update(deleteBSql, new Object[]{TEST_ID_STRING});
        String deleteDSql = "delete from TableD where tableDProperty = ?";
        jdbcTemplate.update(deleteDSql, new Object[]{TEST_ID});
        String deleteESql = "delete from TableE where ID = ?";
        jdbcTemplate.update(deleteESql, new Object[]{TEST_ID});
        String deleteASql = "delete from TableA where tableAPk = ?";
        jdbcTemplate.update(deleteASql, new Object[]{TEST_ID_STRING});
        String deleteCSql = "delete from TableC where ID = ?";
        jdbcTemplate.update(deleteCSql, new Object[]{TEST_ID});
    }

    @Test
    public void test() {
        TableA tableA = repoA.findById(TEST_ID_STRING).get();
        TableC tableC = repoC.findById(TEST_ID).get();
        tableA.getTableBs().add(new TableB(TEST_ID_STRING, tableC, new TableD(tableC.getTableE())));
        TableA updatedTableA = null;
        try {
            updatedTableA = repoA.save(tableA);
        } catch(Exception e) {
            fail("test:"+e.getMessage());
        }

        assertNotNull(updatedTableA);
        assertTrue(Hibernate.isInitialized(updatedTableA.getTableBs().get(0).getTableC()));
    }
}

此测试通过,但是如果您检查返回的对象,则它是具有TableC _ $$ _ jvst值的代理。...然后,我的应用程序尝试序列化此对象(我需要)时,它将崩溃。

1 个答案:

答案 0 :(得分:0)

将您有问题的实体包裹为:

public static <T> T unproxy(T entity){
   Hibernate.initialize(entity)
   if(entity instanceof HibernateProxy){
       entity = (T)((HibernateProxy)entity).getHibernateLazyInitializer().getImplementation();
   } else {
       entity = (T)entity
   }
   return entity;
}

它将返回您未代理的对象。