Phewww..

Alhamdulillah. Setelah stuck hari Jumaat yang lalu kerana id null semasa nak simpan maklumat Merchant ke dalam UploadedFile, akhirnya Tedi jumpa juga cara penyelesaiannya.

Tiba-tiba diilhamkan oleh Allah subhanahu wa ta'ala untuk cari cara bagaimana property refId tu boleh simpan pelbagai jenis entity. Ini adalah kerana pada masa ini refId tu berjenis java.lang.Long sahaja, ia jadi null walaupun dalam satu transaction.

Jadi Tedi terfikir jika Tedi bukan hantar Long, sebaliknya Tedi hantar objek kepada Merchant, patutnya boleh simpan. Jika nak buat macam ni kena declare relationship Merchant dengan property refId tu. Tapi Tedi tak mahu.

Sebab?

Sebab utamanya adalah kerana column ref_id ni nanti akan simpan juga id daripada table lain selain Merchant, pada masa ini mungkin table Agent akan menggunakannya.

Daripada buah fikiran tersebut, Tedi mula meng-Google, dan Tedi dapat tahu mengenai annotation @Any.

Versi

Versi annotation @Any yang Tedi gunakan adalah: org.hibernate.annotations.Any daripada hibernate-core-4.2.11.Final.jar. Manalah tahu pada masa akan datang syntax ini tak boleh pakai lagi.

Entity

@Any( metaColumn = @Column(name = "make_info"), fetch = FetchType.LAZY)
@AnyMetaDef(
metaType = "string",
idType = "java.lang.Long",
metaValues = {
@MetaValue(value = Merchant.MAKER_CHECKER_ENTRIES_MAKE_INFO, targetEntity = Merchant.class),
@MetaValue(value = Agent.MAKER_CHECKER_ENTRIES_MAKE_INFO, targetEntity = Agent.class)
}
)
@JoinColumn(name = "ref_id")
private Serializable refId;

@Transient
private String makeInfo;

...

Nota:

kita tak perlukan property untuk make_info kerana nanti hibernate akan simpan value tersebut menggunakan value daripada @MetaValue yang telah kita tetapkan. make_info adalah nama column dalam table yang akan menjadikan membantu menjadikan ref_id unik, ini adalah kerana tanpa make_info nilai pada ref_id secara umumnya akan berulang kerana ia simpan id daripada pelbagai table.

Cuma dalam kes Tedi ni terpaksa juga buat property makeInfo tapi Tedi set sebagai @Transient kerana Tedi nak pass value pada function lain. Boleh kata sebagai tempat sementara simpan value untuk make_info.

@JoinColumn adalah nama column yang akan simpan id table lain, dan untuk table lain kita tak perlu define column apa yang diorang nak guna. Tapi tak tahulah jika kita boleh customized? Mungkin boleh explore pada masa akan datang.

fetch secara default adalah FetchType.EAGER tapi Tedi override kepada LAZY sebab Tedi takut table Merchant tu heavy, lebih baik Tedi ignore dulu pada masa ini.

Isu Semasa Gunakan @AnyIsu Semasa Gunakan @Any

org.hibernate.MappingException: Unknown entity: java.lang.Long 

Tiba-tiba sahaja seorang kawan Tedi memaklumkan ada masalah semasa gunakan entity UploadedFile yang Tedi telah ubah untuk menggunakan @Any.

Tedi pun uji apa masalah tersebut dan dapat mesej "org.hibernate.MappingException: Unknown entity: java.lang.Long". Masalah ni timbul semasa DAO cuba retrieve rekod UploadedFile.

Kaji punya kaji, Tedi dapati ada 2 masalah iaitu: 

  1. parameter refId yang Tedi declare adalah berjenis Long. Maka Tedi kena tukar kepada Serializable untuk membolehkan developer menghantar entity. Kita tak perlukan ID di sini sebaliknya kena hantar entity yang kita isytihar dalam meta.
  2. Walaupun parameter telah ditukar kepada Serializable, namun ada fungsi yang menghantar parameter berjenis Long dan tiada error semasa compile. Jadi kena aware supaya tidak salah hantar parameter.

Contoh:

public UploadedFile get( String fileType, Serializable refId) {
Criteria criteria = this.getCurrentSession().createCriteria(UploadedFile.class,"uf");
criteria.setReadOnly(true);
criteria.add(Restrictions.allEq(getConstraintsMap(fileType, refId)));
return (UploadedFile) ListUtil.getFirstItem(criteria.list(), null);
}

20200323