Sospensione di un oggetto proxy non funziona per superclasse metodi
-
10-10-2019 - |
Domanda
Abbiamo un'applicazione web che utilizza Sospensione.Dopo l'aggiornamento il codice di base per la Sospensione 3.6 (da 3.3.2) ho scoperto che i dati del proxy oggetti generati da Sospensione solo restituisce il valore corretto per alcuni metodi.Sembra che i metodi di calcestruzzo classe del modello di dati funzionare bene, ma i metodi in @MappedSuperclass
abstract superclassi non funzionano.
Qui è il modello di dati abbiamo:
@MappedSuperclass
public abstract class DataObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, columnDefinition = "serial")
private int id;
// getter, setter, equals, hashcode implementations here
}
@MappedSuperclass
public abstract class SecuredDataObject extends DataObject {
@Version
@Column(name = "Version")
private int version;
@Basic
@Column(name = "SecurityId", nullable = true)
private Integer securityId;
// getters, setters here
}
@MappedSuperclass
public abstract class AuditedDataObject extends SecuredDataObject {
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CreatedDate", nullable = true)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "LastUpdateDate", nullable = true)
private Date lastUpdateDate;
// getters, setters here
}
@Entity
@Table(name = "Form")
public class Form extends AuditedDataObject {
@Basic
@Column(name = "Name", length = 200, nullable = false)
private String name;
@Basic
@Column(name = "Path", length = 80, nullable = true)
private String path;
// getters, setters, other properties and methods here
}
Questo ha funzionato bene in Sospensione 3.3.2, ma dopo l'aggiornamento a Sospensione 3.6 l'applicazione è andato storto.Il codice test riportato di seguito viene illustrato il problema:
int formId = 1234;
Form form = (Form) sessionFactory.getCurrentSession().load(Form.class, formId);
System.out.print("id = ");
System.out.println(formId);
System.out.print("getId() = ");
System.out.println(form.getId());
System.out.print("getSecurityId() = ");
System.out.println(form.getSecurityId());
System.out.print("getVersion() = ");
System.out.println(form.getVersion());
System.out.print("getLastUpdateDate() = ");
System.out.println(form.getLastUpdateDate());
System.out.print("getCreatedDate() = ");
System.out.println(form.getCreatedDate());
System.out.print("getName() = ");
System.out.println(form.getName());
System.out.print("getPath() = ");
System.out.println(form.getPath());
L'output di tale codice è:
id = 1234
getId() = 0
getSecurityId() = 182
getVersion() = 0
getLastUpdateDate() = null
getCreatedDate() = null
getName() = Form name here
getPath() = /path/here
Quattro di questi metodi hanno restituito risultati non corretti:getId(), getVersion(), getLastUpdateDate() e getCreatedDate() ha restituito 0 o null.L'attuale riga nel database è non-zero / valori non nulli.Tuttavia getName(), getPath() e, più curiosamente getSecurityId() hanno lavorato bene.
Qualcuno può spiegare perché questo sta accadendo?È un problema fondamentale con il mappato superclassi, oppure c'è un altro motivo per cui questo potrebbe accadere?
Si noti che il Form
oggetto restituito da Hibernate è un Javassist proxy, se vista in un debugger, in genere, un nome di classe come Form_$$_javassist_15
ecc.
Aggiornamento:
Questo problema sembra essere che si verificano in modalità di Ibernazione, non Javassist.Ho acceso il bytecode generazione di CGLIB impostando hibernate.bytecode.provider=cglib
in modalità di ibernazione.proprietà, ma ottenere esattamente gli stessi risultati errati con CGLIB (e confermato CGLIB sta lavorando perché il nome della classe restituito dalla Sospensione diventa Form$$EnhancerByCGLIB$$4f3b4523
).
Io sono ancora più vicini a identificare il motivo per cui è sbagliato, però.
Soluzione
Ho trovato la risposta:alcuni dei metodi getter e setter sul superclassi sono stati contrassegnati final
.
Col senno di poi, questo è ovvio...perché i metodi sono stati finale, le classi proxy non poteva ignorarle.Quindi la soluzione è rimuovere final
da qualsiasi getter e setter in mappato superclassi.
Qui ci sono i getter e i setter sono stati definiti sulla SecuredDataObject
:
@MappedSuperclass
public abstract class SecuredDataObject extends DataObject {
@Version
@Column(name = "Version")
private int version;
@Basic
@Column(name = "SecurityId", nullable = true)
private Integer securityId;
// Note - method IS NOT final
public Integer getSecurityId() {
return securityId;
}
public void setSecurityId(Integer securityId) {
this.securityId = securityId;
}
// Note - method IS final
public final int getVersion() {
return version;
}
public final void setVersion(final int version) {
this.version = version;
}
}
Ciò spiega perché il mio test era di ritorno securityId
correttamente, ma non era di ritorno version
correttamente — getVersion()
era final
considerando che getSecurityId()
non era.
Quindi, in sintesi, non segnare il tuo getter e setter come final
se la Sospensione potrebbe tentare di proxy loro!