|
After following an example (http://docs.jboss.org/hibernate/core/4.3/manual/en-US/html_single/#d5e2288) on how to create a composite ID that contained a foreign composite ID, I ran into some issues with the data. Based on that example, the JPA supported method will be referred to as Option 1 (the one I want to use) and the other is Option 2. This issue is with Option 1.
The database and models were made to test various Hibernate scenarios, so I apologize if they are a bit harder to follow.
I have the following data (scripts have been attached to recreate):
id1 id2 description
0 0 CK0
0 1 CK1
1 0 CK2
1 1 CK3
2 0 CK4
2 1 CK5
primaryId compositeId1 compositeId2 description
0 0 0 PCK0
0 0 1 PCK1
0 1 0 PCK2
0 1 1 PCK3
0 2 0 PCK4
0 2 1 PCK5
We created a custom annotation to handle Hibernate @Entity defaults for our models:
@Entity(dynamicUpdate = true, optimisticLock = OptimisticLockType.ALL)
public @interface HibernateDefaultEntity {
}
and the following models:
@Entity
@HibernateDefaultEntity
@Table(name = "PrimaryKey")
@Proxy(lazy = false)
public class PrimaryKeyModel implements Serializable {
@Id
@Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "PrimaryKeyModel{" + "id=" + id + ", description=" + description + '}';
}
private Integer id;
private String description;
}
@Entity
@HibernateDefaultEntity
@Table(name = "CompositeKey")
@Proxy(lazy = false)
public class CompositeKeyModel implements Serializable {
@EmbeddedId
public CompositeKeyModelId getId() {
return id;
}
public void setId(CompositeKeyModelId id) {
this.id = id;
}
@Column(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int hashCode() {
int hash = 5;
hash = 23 * hash + (this.id != null ? this.id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CompositeKeyModel other = (CompositeKeyModel)obj;
return this.id == other.id || (this.id != null && this.id.equals(other.id));
}
@Override
public String toString() {
return "CompositeKeyModel{" + "id=" + id + ", description=" + description + '}';
}
private CompositeKeyModelId id;
private String description;
}
@Embeddable
public class CompositeKeyModelId implements Serializable {
public CompositeKeyModelId() {
}
public CompositeKeyModelId(Integer id1, Integer id2) {
this.id1 = id1;
this.id2 = id2;
}
@Column(name = "id1")
public Integer getId1() {
return id1;
}
public void setId1(Integer id1) {
this.id1 = id1;
}
@Column(name = "id2")
public Integer getId2() {
return id2;
}
public void setId2(Integer id2) {
this.id2 = id2;
}
@Override
public int hashCode() {
int hash = 5;
hash = 53 * hash + (this.id1 != null ? this.id1.hashCode() : 0);
hash = 53 * hash + (this.id2 != null ? this.id2.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CompositeKeyModelId other = (CompositeKeyModelId)obj;
if (this.id1 != other.id1 && (this.id1 == null || !this.id1.equals(other.id1))) {
return false;
}
return this.id2 == other.id2 || (this.id2 != null && this.id2.equals(other.id2));
}
@Override
public String toString() {
return "CompositeKeyModelId{" + "id1=" + id1 + ", id2=" + id2 + '}';
}
private Integer id1;
private Integer id2;
}
@Entity
@HibernateDefaultEntity
@Table(name = "PrimaryCompositeKey")
@Proxy(lazy = false)
public class PrimaryCompositeKeyOption1Model implements Serializable {
@EmbeddedId
public PrimaryCompositeKeyOption1ModelId getId() {
return id;
}
public void setId(PrimaryCompositeKeyOption1ModelId id) {
this.id = id;
}
@Column(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@MapsId(value = "primaryId")
@JoinColumn(name = "primaryId")
@ManyToOne(fetch = FetchType.LAZY)
public PrimaryKeyModel getPrimaryKeyModel() {
return primaryKeyModel;
}
public void setPrimaryKeyModel(PrimaryKeyModel primaryKeyModel) {
this.primaryKeyModel = primaryKeyModel;
}
@MapsId("compositeKeyModelId")
@JoinColumns({
@JoinColumn(name = "compositeId1", referencedColumnName = "id1"),
@JoinColumn(name = "compositeId2", referencedColumnName = "id2")
})
@ManyToOne(fetch = FetchType.LAZY)
public CompositeKeyModel getCompositeKeyModel() {
return compositeKeyModel;
}
public void setCompositeKeyModel(CompositeKeyModel compositeKeyModel) {
this.compositeKeyModel = compositeKeyModel;
}
@Override
public String toString() {
return "PrimaryCompositeKeyOption1Model{" + "id=" + id + ", description=" + description + '}';
}
private PrimaryCompositeKeyOption1ModelId id = new PrimaryCompositeKeyOption1ModelId();
private PrimaryKeyModel primaryKeyModel;
private CompositeKeyModel compositeKeyModel;
private String description;
}
@Embeddable
public class PrimaryCompositeKeyOption1ModelId implements Serializable {
public PrimaryCompositeKeyOption1ModelId() {
}
public PrimaryCompositeKeyOption1ModelId(Integer primaryId, Integer compositeId1, Integer compositeId2) {
this.primaryId = primaryId;
this.compositeKeyModelId = new CompositeKeyModelId(compositeId1, compositeId2);
}
@Column(name = "primaryId")
public Integer getPrimaryId() {
return primaryId;
}
public void setPrimaryId(Integer primaryId) {
this.primaryId = primaryId;
}
public CompositeKeyModelId getCompositeKeyModelId() {
return compositeKeyModelId;
}
public void setCompositeKeyModelId(CompositeKeyModelId compositeKeyModelId) {
this.compositeKeyModelId = compositeKeyModelId;
}
@Override
public int hashCode() {
int hash = 3;
hash = 37 * hash + (this.primaryId != null ? this.primaryId.hashCode() : 0);
hash = 37 * hash + (this.compositeKeyModelId != null ? this.compositeKeyModelId.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PrimaryCompositeKeyOption1ModelId other = (PrimaryCompositeKeyOption1ModelId)obj;
if (this.primaryId != other.primaryId && (this.primaryId == null || !this.primaryId.equals(other.primaryId))) {
return false;
}
return this.compositeKeyModelId == other.compositeKeyModelId || (this.compositeKeyModelId != null && this.compositeKeyModelId.equals(other.compositeKeyModelId));
}
@Override
public String toString() {
return "PrimaryCompositeKeyOption1ModelId{" + "primaryId=" + primaryId + ", compositeKeyModelId=" + compositeKeyModelId + '}';
}
private Integer primaryId;
private CompositeKeyModelId compositeKeyModelId = new CompositeKeyModelId();
}
Now, when I use the following test:
GenericApplicationContext context = new GenericXmlApplicationContext("com/geowareinc/shared/spring/test.context.xml");
SessionFactory sessionFactory = (SessionFactory)context.getBean("sessionFactory");
Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
List<PrimaryCompositeKeyOption1Model> models = session.createCriteria(PrimaryCompositeKeyOption1Model.class).list();
transaction.commit();
for (PrimaryCompositeKeyOption1Model model : models) {
System.out.println(model);
System.out.println(model.getPrimaryKeyModel());
System.out.println(model.getCompositeKeyModel());
System.out.println();
}
My output looks like:
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=0, id2=0}}, description=PCK0}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=0, id2=0}, description=CK0}
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=0, id2=0}}, description=PCK0}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=0, id2=0}, description=CK0}
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=1, id2=1}}, description=PCK2}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=1, id2=0}, description=CK2}
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=1, id2=1}}, description=PCK2}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=1, id2=0}, description=CK2}
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=2, id2=2}}, description=PCK4}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=2, id2=0}, description=CK4}
PrimaryCompositeKeyOption1Model{id=PrimaryCompositeKeyOption1ModelId{primaryId=0, compositeKeyModelId=CompositeKeyModelId{id1=2, id2=2}}, description=PCK4}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=2, id2=0}, description=CK4}
The CompositeKeyModelId referenced by the PrimaryCompositeKeyModelId does not contain the same data as the CompositeKeyModelId referenced by the CompositeKeyModel association. Additionally, the descriptions for both the PrimaryCompositeKeyModel and CompositeKeyModel is the same as the previous model every other model (i.e. no PCK1/CK1, PCK3/CK3, or PCK5/CK5). Bizarre.
I checked the query being generated, and it is selecting compositeId1 twice:
select this_.compositeId1 as composit0_2_0_, this_.primaryId as primaryI1_2_0_, this_.compositeId1 as composit3_2_0_, this_.compositeId2 as composit4_2_0_, this_.description as descript2_2_0_
from PrimaryCompositeKey this_
Other than the extra column, the data it returns is correct:
composit0_2_0_ primaryI1_2_0_ composit3_2_0_ composit4_2_0_ descript2_2_0_
0 0 0 0 PCK0
0 0 0 1 PCK1
1 0 1 0 PCK2
1 0 1 1 PCK3
2 0 2 0 PCK4
2 0 2 1 PCK5
This does not happen with Option 2:
@Entity
@HibernateDefaultEntity
@Table(name = "PrimaryCompositeKey")
@Proxy(lazy = false)
public class PrimaryCompositeKeyOption2Model implements Serializable {
@EmbeddedId
public PrimaryCompositeKeyOption2ModelId getId() {
return id;
}
public void setId(PrimaryCompositeKeyOption2ModelId id) {
this.id = id;
}
@Column(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "PrimaryCompositeKeyOption2Model{" + "description=" + description + '}';
}
private PrimaryCompositeKeyOption2ModelId id;
private String description;
}
@Embeddable
public class PrimaryCompositeKeyOption2ModelId implements Serializable {
public PrimaryCompositeKeyOption2ModelId() {
}
public PrimaryCompositeKeyOption2ModelId(PrimaryKeyModel primayKeyModel, CompositeKeyModel compositeKeyModel) {
this.primayKeyModel = primayKeyModel;
this.compositeKeyModel = compositeKeyModel;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "primaryId")
public PrimaryKeyModel getPrimayKeyModel() {
return primayKeyModel;
}
public void setPrimayKeyModel(PrimaryKeyModel primayKeyModel) {
this.primayKeyModel = primayKeyModel;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "compositeId1"),
@JoinColumn(name = "compositeId2")
})
public CompositeKeyModel getCompositeKeyModel() {
return compositeKeyModel;
}
public void setCompositeKeyModel(CompositeKeyModel compositeKeyModel) {
this.compositeKeyModel = compositeKeyModel;
}
@Override
public int hashCode() {
int hash = 7;
hash = 97 * hash + (this.primayKeyModel != null ? this.primayKeyModel.hashCode() : 0);
hash = 97 * hash + (this.compositeKeyModel != null ? this.compositeKeyModel.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PrimaryCompositeKeyOption2ModelId other = (PrimaryCompositeKeyOption2ModelId)obj;
if (this.primayKeyModel != other.primayKeyModel && (this.primayKeyModel == null || !this.primayKeyModel.equals(other.primayKeyModel))) {
return false;
}
return this.compositeKeyModel == other.compositeKeyModel || (this.compositeKeyModel != null && this.compositeKeyModel.equals(other.compositeKeyModel));
}
private PrimaryKeyModel primayKeyModel;
private CompositeKeyModel compositeKeyModel;
}
PrimaryCompositeKeyOption2Model{description=PCK0}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=0, id2=0}, description=CK0}
PrimaryCompositeKeyOption2Model{description=PCK1}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=0, id2=1}, description=CK1}
PrimaryCompositeKeyOption2Model{description=PCK2}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=1, id2=0}, description=CK2}
PrimaryCompositeKeyOption2Model{description=PCK3}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=1, id2=1}, description=CK3}
PrimaryCompositeKeyOption2Model{description=PCK4}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=2, id2=0}, description=CK4}
PrimaryCompositeKeyOption2Model{description=PCK5}
PrimaryKeyModel{id=0, description=PK0}
CompositeKeyModel{id=CompositeKeyModelId{id1=2, id2=1}, description=CK5}
select this_.compositeId1 as composit3_2_0_, this_.compositeId2 as composit4_2_0_, this_.primaryId as primaryI1_2_0_, this_.description as descript2_2_0_
from PrimaryCompositeKey this_
composit3_2_0_ composit4_2_0_ primaryI1_2_0_ descript2_2_0_
0 0 0 PCK0
0 1 0 PCK1
1 0 0 PCK2
1 1 0 PCK3
2 0 0 PCK4
2 1 0 PCK5
As you can see, the extra column doesn't exist in this scenario, which leads me to believe it is the culprit.
|