Hibernate composite key which are foreigen key to another table - java

I have two table Part and SubPart. Part table has general fields like id, name, desc etc. The SubPart table has part_id, sub_part_id as composite key. Both of these columns are referring to Part table and has a one to many mapping for each of them, like for each part_id in the Part table there can be multiple entries in SubPart table for both the columns. I'm having problem defining the composite key for the SubPart table. I tried the Embedded tag but its not working. How can I address this problem. Thanks a lot.
Part table like this.
#Entity
#Table(name="Part")
public class Part {
#Id
#GeneratedValue
#Column(name="Part_Id")
private int id;
#Column(name="Part_Number")
private String partNumber;
#Column(name="Part_Name")
private String partName;
}
Sub Part Table
#Entity
#Table(name="SubPart")
public class SubPart {
// part and subPart combination is the compound key here.
#ManyToOne
#JoinColumn(name="Part_Id")
private Part part;
#ManyToOne
#JoinColumn(name="Sub_Part_Id")
private Part subPart;
#Column(name="Quantity")
private Integer quantity;
}

You said
I'm having problem defining the composite key for the SubPart table
When you have a compound primary key, you must define a class (Usually a static inner class) which defines your compound primery key (Just an advice: because Hibernate makes use of proxies, prefer to put your annotated mapping on the getter's instead of field members)
/**
* When both entity class and target table SHARE the same name
* You do not need #Table annotation
*/
#Entity
public class SubPart implements Serializable {
#EmbeddedId
private SubPartId subPartId;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="PART_ID", insertable=false, updateable=false)
private Part part;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="SUP_PART_ID", insertable=false, updateable=false)
private SubPart subPart;
/**
* required no-arg constructor
*/
public SubPart() {}
public SubPart(SubPartId subPartId) {
this.subPartId = subPartId;
}
// getter's and setter's
/**
* It MUST implements Serializable
* It MUST overrides equals and hashCode method
* It MUST has a no-arg constructor
*
* Hibernate/JPA 1.0 does not support automatic generation of compound primary key
* You SHOULD set up manually
*/
#Embeddable
public static class SubPartId implements Serializable {
#Column(name="PART_ID", updateable=false, nullable=false)
private Integer partId;
#Column(name="SUB_PART_ID", updateable=false, nullable=false)
private Integer subPartId;
/**
* required no-arg constructor
*/
public SubPartId() {}
public SubPartId(Integer partId, Integer subPartId) {
this.partId = partId;
this.subPartId = subPartId;
}
// getter's and setter's
#Override
public boolean equals(Object o) {
if(!(o instanceof SubPartId))
return null;
final SubPartId other = (SubPartId) o;
return new EqualsBuilder().append(getPartId(), other.getPartId())
.append(getSubPartId(), other.getSubPartId())
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder().append(getPartId())
.append(getSubPartId())
.toHashCode();
}
}
}
Notice Part and SubPart mapping has been marked as insertable=false, updateable=false Because the mapping has been defined in the compound primary key. Hibernate does not allow you mapping two properties WITH THE SAME COLUMN unless you mark insertable=false, updateable=false. Otherwise you will see this nice exception
Should be marked with insertable=false, updateable=false

I think I would declare a field of type Map<Part,SubPart> in class Part and declare it as #OneToMany.
Actually, in this particular case, it could event be a Map<Part,Integer> because the only other field is the quantity. The SubPart class is not needed.

Related

SQL JPA - Multiple columns as primary key

If i want severeal Column to make up an ID.
SQL example :
CONSTRAINT [PK_NAME] PRIMARY KEY ([Column1],[Column2],[Column3])
How can i do that with a Jpa Entity class ? through columndefinition ?
just setting the id field as:
value = Column1 + Column2 + Column3 // aint working.
You need to have a class for your composite key:
public class CompositeKey implements Serializable {
private int column1;
private int column2;
private int column3;
}
and then in your entity class use the #IdClass annotation:
#Entity
#IdClass(CompositeKey.class)
public class EntityExample {
#Id
private int column1;
#Id
private int column2;
#Id
private int column3;
...
...
}
I think this should work.
There is also the other solution that #jklee mentioned. Both work, it's a matter of preference.
Use #Embeddable and #EmbeddedId.
Example:
#Entity
public class Project implements Serializable {
#EmbeddedId ProjectId id;
}
#Embeddable
class ProjectId implements Serializable {
int departmentId;
long projectId;
}
More information here http://www.objectdb.com/java/jpa/entity/id#Embedded_Primary_Key_
If all fields in the class are part of primary key, then solution would be pretty simple (extending solution provided by #raul-cuth):
#Entity
#IdClass(EntityExample.class)
public class EntityExample implements Serializable {
#Id
private int column1;
#Id
private int column2;
#Id
private int column3;
}
Using #IdClass annotation on the #Entity class followed by #Id annotation on individual fields that are part of composite primary key.
Alternatively can make use of #Embeddable class which can consist of individual fields of the composite primary key and then a reference of this class can be used as an attribute with #Embedded annotation in #Entity class.
Hope this helps.
Be aware that the hibernate Entity-Class-to-SQL-DDL-Script generator will sort all the fields, and irrespective of the order in which it appears in the definitions, will create the table definition and the index / constraint definitions in this sorted order of the fields.
While the order of appearance of the fields in the table definition may not matter much, the order of fields in a composite index definitely do. So your key-fields must be named so that when sorted by their names they are in the order you desire for the index).

Hibernate. Generic Composite PK issue

I have an issue with composite primary key in Hibernate.
Example:
I have a class that represents base primary key for all instances of this.
public abstract class PrimaryKey implements Serializable { /* ... */ }
It has nothing but implements java.io.Serializable interface and can be used in generics or another class's methods as parameter to narrow an accepted classes.
Another primary key class should inherit it and add your specific fields as keys. For example:
public class PassportPK extends PrimaryKey {
private String number;
private String series;
public PassportPK() {}
public PassportPK(String number, String series) {
this.number = number;
this.series = series;
}
// Getters/setters are below.
}
Then it's used in the appropriate entity like this:
#Entity
#Table(name = "T_PASSPORTS")
#IdClass(PassportPK.class)
public class Passport implements Serializable {
#Id
#Column(name = "F_NUMBER")
private String number;
#Id
#Column(name = "F_SERIES")
private String series;
public Passport() {}
// Getters/setters are below.
}
Everything works fine if I have a deal with entities like this.
But some entities in my project have a simple primary key like int, long, String, and etc.
In this case I'd like to have a generic primary key like this one:
public class SimplePK<T extends Serializable> extends PrimaryKey {
/**
* Represents a simple key field of entity (i.e. int, long, String, ...);
*/
private T id;
public SimplePK() {}
public SimplePK(T id) {
this.id = id;
}
public T getId() {
return this.id;
}
public void setId(T id) {
this.id = id;
}
}
QUESTION: How can I resolve it in annotation mapping way?
p.s. When I'm trying to resolve it like in the previous example (via #IdClass(SimplePK.class) I catch a "org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application-context.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Property com.testprj.entity.SimplePK.id has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute (eg #OneToMany(target=) or use an explicit #Type" exception.
p.p.s. I use Hibernate with Spring framework for wiring components.
I would be grateful for any help!
I think that you can't use a generic type inside the id Class.
Used the #IdClass to indicate the Class for the Composite Primary Key, if you want to used only one attribute like primary key you have to use #Id over the declaration and remove #IdClass.
Example:
#Entity
#Table(name = "T_PASSPORTS")
public class Passport implements Serializable {
#Id
private String id; //Or int, long...
public Passport() {}
// Getters/setters are below.
}

How to create a generic entity model class that supports generic id including auto generated ids?

I have three kinds of primary keys for tables:
INT auto generated primary key which use AUTO_INCREMENT capacity from database vendor (MySQL)
CHAR(X) primary key to store a user readable value as key (where X is a number and 50 <= X <= 60)
Complex primary keys, composed by 2 or 3 fields of the table.
Also, there are some group of fields that may be present (or not):
version, INT field.
createdBy, VARCHAR(60) field, and lastUpdatedBy, VARCHAR(60) field (there are more fields but these covers a basic example).
Some examples of above:
Table1
id int primary key auto_increment
version int
value char(10)
createdBy varchar(60)
lastUpdatedBy varchar(60)
Table2
id char(60) primary key
shortDescription varchar(20)
longDescription varchar(100)
Table3
field1 int primary key
field2 int primary key
amount decimal(10, 5)
version int
With all this in mind, I need to create a generic set of classes that supports these requirements and allows CRUD operations using Hibernate 4.3 and JPA 2.1.
Here's my current model (getters/setters avoided to shorten the code sample):
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
}
#MappedSuperclass
public abstract class VersionedEntity<T> extends BaseEntity<T> {
#Version
protected int version;
}
#MappedSuperclass
public abstract class MaintainedEntity<T> extends VersionedEntity<T> {
#Column
protected String createdBy;
#Column
protected String lastUpdatedBy;
}
#Entity
public class Table1 extends MaintainedEntity<Long> {
#Column
private String value;
}
#Entity
public class Table2 extends BaseEntity<String> {
#Column
private String shortDescription;
#Column
private String longDescription;
}
I'm currently testing save instances of Table1 and Table2. I have the following code:
SessionFactory sf = HibernateUtils.getSessionFactory();
Session session = sf.getCurrentSession();
session.beginTransaction();
Table1 newTable1 = new Table1();
newTable1.setValue("foo");
session.save(newTable1); //works
Table2 newTable2 = new Table2();
//here I want to set the ID manually
newTable2.setId("foo_id");
newTable2.setShortDescription("short desc");
newTable2.setLongDescription("long description");
session.save(newTable2); //fails
session.getTransaction().commit();
sf.close();
It fails when trying to save Table2 and I get the following error:
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
The error message is obvious because a CHAR(X) field doesn't have a default value and won't have it (AFAIK). I tried changing the generation strategy to GenerationType.AUTO and got the same error message.
How can I remodel these classes in order to support these requirements? Or even better, how could I provide a generation strategy that depends on the key of the entity I'm saving, which could be auto generated or provided by me?
Involved technologies:
Java SDK 8
Hibernate 4.3.6
JPA 2.1
MySQL and Postgres databases
OS: Windows 7 Professional
Note: the above may (and probably will) change in order to be supported for other implementations of JPA 2.1 like EclipseLink.
Did not try this, but according to Hibernate's api this should not be complicated by creating custom implementation of IdentityGenerator.
It's generate method gets and object for which you are generating the value so you can check the type of the id field and return appropriate value for your primary key.
public class DynamicGenerator implements IdentityGenerator
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if (shouldUseAutoincrementStartegy(object)) { // basing on object detect if this should be autoincrement or not, for example inspect the type of id field by using reflection - if the type is Integer use IdentityGenerator, otherwise another generator
return new IdentityGenerator().generate(seession, object)
} else { // else if (shouldUseTextKey)
String textKey = generateKey(session, object); // generate key for your object
// you can of course connect to database here and execute statements if you need:
// Connection connection = session.connection();
// PreparedStatement ps = connection.prepareStatement("SELECT nextkey from text_keys_table");
// (...)
return textKey;
}
}
}
Having this simply use it as your generation strategy:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GenericGenerator(name="seq_id", strategy="my.package.DynamicGenerator")
protected T id;
}
For Hibernate 4, you should implement IdentifierGenerator interface.
As above is accepted for Hibernate it should be still possible to create it in more generic way for any "jpa compliant" provider. According to JPA api in GeneratedValue annotation you can provide your custom generator. This means that you can provide the name of your custom generator and you should implement this generator for each jpa provider.
This would mean you need to annotate BaseEntity with following annotation
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(generator="my-custom-generator")
protected T id;
}
Now you need to register custom generator with name "my-custom-generator" for each jpa provider you would like to use.
For Hibernate this is surly done by #GenericGenerator annotation as shown before (adding #GenericGenerator(name="my-custom-generator", strategy="my.package.DynamicGenerator" to BaseEntity class on either id field or BaseEntity class level should be sufficient).
In EclipseLink I see that you can do this via GeneratedValue annotation and registering it via SessionCustomizer:
properties.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
"my.custom.CustomIdGenerator");
public class CustomIdGenerator extends Sequence implements SessionCustomizer {
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return "Id"; // generate the id
}
#Override
public Vector getGeneratedVector(Accessor accessor,
AbstractSession writeSession, String seqName, int size) {
return null;
}
#Override
protected void onConnect() {
}
#Override
protected void onDisconnect() {
}
#Override
public boolean shouldAcquireValueAfterInsert() {
return false;
}
#Override
public boolean shouldOverrideExistingValue(String seqName,
Object existingValue) {
return ((String) existingValue).isEmpty();
}
#Override
public boolean shouldUseTransaction() {
return false;
}
#Override
public boolean shouldUsePreallocation() {
return false;
}
public void customize(Session session) throws Exception {
CustomIdGenerator sequence = new CustomIdGenerator ("my-custom-generator");
session.getLogin().addSequence(sequence);
}
}
Each provider must give a way to register id generator, so you would need to implement and register custom generation strategy for each of the provider if you want to support all of them.
You can "workaround" this forcing derived class to implement method which will ensure the Id is assigned and annotate this method with #PrePersist. You can provide default implementation for classes for which the Id will be auto generated.
Somethig like:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
#PrePersist
public void ensureIdAssigned() {
ensureIdAssignedInternal();
}
public abstract void ensureIdAssignedInternal();
}
#MappedSuperclass
public abstract class AutoIdMaintaintedEntity<T> extends MaintainedEntity<T> { // provide default implementation for Entities with Id generated by #GeneratedValue(strategy=GenerationType.IDENTITY) on BaseEntity superclass
public void ensureIdAssignedInternal() {
// nothing here since the Id will be automatically assigned
}
}
#Entity
public class Table1 extends AutoIdMaintaintedEntity<Long> {
#Column
private String value;
}
#Entity
public class Table2 extends BaseEntity<String> {
#Column
private String shortDescription;
#Column
private String longDescription;
public void ensureIdAssignedInternal() {
this.id = generateMyTextId();
}
private String generateMyTextId() {
return "text id";
}
}
Inheritance hierarchies fight ORM.
So keep things simple and stay a little closer to the database implementation. Don't map a hierarchy of abstract superclasses for this, but embed annotated POJO's for chunks of shared columns. They may well turn out to be handy to work with in the rest of your code as well.
Create #Embeddable classes for the shared fields, and make the class for your composite ID #Embeddable too.
#Embeddable
public class Maintained implements Serializable{
private String maintainedBy;
private String updatedBy;
// getters and setters
}
#Embeddable
public class CompositeId implements Serializable{
#Column
private int id1;
#Column
private int id2;
...
}
The simplest version of your implementation classes then look like this:
#Entity
public class Table1 {
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Id
protected Long id;
#Version
private String version;
#Embedded
private Maintained maintained;
...
public Maintained getMaintained(){
return maintained;
}
}
For the String ID, no auto generation:
#Entity
public class Table2 {
#Id
private String id;
#Column
private String shortDescription;
#Column
private String longDescription;
...
}
And the composite ID as #EmbeddedId:
#Entity
public class Table3 {
#EmbeddedId
private CompositeId id;
#Version
private String version;
#Column
private int amount;
...
}
As an extra benefit, you can mix and match adding more of these traits if you like, since you're no longer constrained by the inheritance tree.
(But you can keep the hierarchy in place containing getters and setters and default delegates if existing code relies on it and/or benefits from it.)

Hibernate Exception: Unable to find properties ... in entity annotated with #IdClass

I have this composite key class:
#Embeddable
public class TaskEntityId implements Serializable {
private static final long serialVersionUID = 1L;
public String objectUuid;
#Column(name="DOMAIN_OBJECT_UUID", nullable=false, length=36)
public String getObjectUuid() { return objectUuid; }
public void setObjectUuid(String uuid) { this.objectUuid = uuid; }
public String taskName;
#Column(name = "TASK_NAME", nullable=false, length=32)
public String getTaskName() { return taskName; }
public void setTaskName(String taskName) { this.taskName = taskName; }
public Date createdTimestamp = new Date();
#Column(name = "CREATED_TS", nullable=false, updatable=false)
#Temporal(TemporalType.TIMESTAMP)
public Date getCreatedTimestamp() { return createdTimestamp; }
public void setCreatedTimestamp(Date createdTimestamp) { this.createdTimestamp = createdTimestamp; }
#Override
public boolean equals(Object o) {...}
#Override
public int hashCode() {...}
#Override
public String toString() {...}
}
Which is used in this entity class:
#Entity
#Table(name = "TASK")
#IdClass(TaskEntityId.class)
public class TaskEntity implements Serializable {
#EmbeddedId
TaskEntityId id;
public TaskEntityId getId() { return id; }
public void setId(TaskEntityId id) { this.id = id; }
...
I also added to this class some extra getters based on suggestions to other similar questions:
public String getObjectUuid() { return id.getObjectUuid(); }
public String getTaskName() { return id.getTaskName(); }
public Date getCreatedTimestamp() { return id.getCreatedTimestamp(); }
Still, I get this exception:
Error in custom provider, org.hibernate.AnnotationException: Unable to find properties (createdTimestamp, objectUuid, taskName) in entity annotated with #IdClass:c.m.d.p.TaskEntity
The solution is to remove the
#IdClass(TaskEntityId.class)
annotation from the TaskEntity class. And I also needed an empty default constructor.
To map a composite key, you can use the EmbeddedId or the IdClass annotations. I know this question is not strictly about JPA but the rules defined by the specification also applies. So here they are:
2.1.4 Primary Keys and Entity Identity
...
A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId and IdClass annotations are used to denote composite primary keys. See sections 9.1.14 and 9.1.15.
...
The following rules apply for composite primary keys:
The primary key class must be public and must have a public no-arg constructor.
If property-based access is used, the properties of the primary key class must be public or protected.
The primary key class must be serializable.
The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped.
A composite primary key must either be represented and mapped as an embeddable class (see Section 9.1.14, “EmbeddedId Annotation”) or must be represented and mapped to multiple fields or properties of the entity class (see Section 9.1.15, “IdClass Annotation”).
If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key fields or properties in the primary key class and those of the entity class must correspond and their types must be the same.
With an IdClass
The class for the composite primary key could look like (could be a static inner class):
public class TimePK implements Serializable {
protected Integer levelStation;
protected Integer confPathID;
public TimePK() {}
public TimePK(Integer levelStation, String confPathID) {
this.id = levelStation;
this.name = confPathID;
}
// equals, hashCode
}
And the entity:
#Entity
#IdClass(TimePK.class)
class Time implements Serializable {
#Id
private Integer levelStation;
#Id
private Integer confPathID;
private String src;
private String dst;
private Integer distance;
private Integer price;
// getters, setters
}
The IdClass annotation maps multiple fields to the table PK.
With EmbeddedId
The class for the composite primary key could look like (could be a static inner class):
*
#Embeddable
public class TimePK implements Serializable {
protected Integer levelStation;
protected Integer confPathID;
public TimePK() {}
public TimePK(Integer levelStation, String confPathID) {
this.id = levelStation;
this.name = confPathID;
}
// equals, hashCode
}
*
And the entity:
#Entity
class Time implements Serializable {
#EmbeddedId
private TimePK timePK;
private String src;
private String dst;
private Integer distance;
private Integer price;
//...
}
The #EmbeddedId annotation maps a PK class to table PK.
Differences:
From the physical model point of view, there are no differences
EmbeddedId somehow communicates more clearly that the key is a composite key and IMO makes sense when the combined pk is either a meaningful entity itself or it reused in your code.
#IdClass is useful to specify that some combination of fields is unique but these do not have a special meaning.
They also affect the way you write queries (making them more or less verbose):
with IdClass
select t.levelStation from Time t
with EmbeddedId
select t.timePK.levelStation from Time t

JPA mapped model returns null elements with Composite-Keys

I have build my data model using JPA and am using Hibernate's EntityManager 3 to access the data. I used HSQLDB for testing (junit). I am using this configuration for other classes and have had no problems.
However, the latest batch of tables use a composite-key as the primary-key and I am not able to retrieve the populated row from the database when it is implemented. I don't get an error, the query simply returns null objects.
For example if I query (using jsql) "FROM Order o" to return a list of all orders in the table, my list.size() has the proper number of elements (2), but the elements are null.
I am hoping that someone with a sharper eye than I can discern what I am doing wrong. Thanks in advance!
The (simplified) tables are defined as:
CREATE TABLE member (
member_id INTEGER NOT NULL IDENTITY PRIMARY KEY);
CREATE TABLE orders (
orders_id INTEGER NOT NULL,
member_id INTEGER NOT NULL,
PRIMARY KEY(orders_id, member_id));
ALTER TABLE orders
ADD CONSTRAINT fk_orders_member
FOREIGN KEY (member_id) REFERENCES member(member_id);
The entity POJOs are defined by:
#Entity
public class Member extends Person implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
#OneToMany(fetch=FetchType.LAZY, mappedBy="member", cascade=CascadeType.ALL)
private Set<Order> orderList;
}
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Id
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
#ManyToOne(optional=false, fetch=FetchType.LAZY)
#JoinColumn(name="MEMBER_ID", nullable=false)
private Member member;
#OneToMany(mappedBy="order", fetch=FetchType.LAZY)
private Set<Note> noteList;
}
OrderPK defines a default constructor and 2 properties (orderId, memberId) along with their get/set methods.
public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;
private Integer orderId;
private Integer memberId;
public OrderPK() {}
public OrderPK(Integer orderId, Integer memberId) {
this.orderId = orderId;
this.memberId = memberId;
}
/**Getters/Setters**/
#Override
public int hashCode() {
return orderId.hashCode() + memberId.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof OrderPK))
return false;
OrderPK other = (OrderPK) obj;
if (memberId == null) {
if (other.memberId != null) return false;
} else if (!memberId.equals(other.memberId))
return false;
if (orderId == null) {
if (other.orderId != null) return false;
} else if (!orderId.equals(other.orderId))
return false;
return true;
}
}
(sorry for the length)
the entityManager is instantiated in an abstract class which is then extended by my other DAOs
protected EntityManager em;
#PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
and is configured by a spring context configuration file
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
</bean>
My test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class OrderDaoTest {
#Autowired
protected OrderDao dao = null;
#Test
public void findAllOrdersTest() {
List<Order> ol = dao.findAll();
assertNotNull(ol); //pass
assertEquals(2, ol.size(); //pass
for (Order o : ol) {
assertNotNull(o); //fail
...
}
}
}
When I strip away the composite-key from the Order class I am able to retrieve data, I am not sure what I am doing incorrectly with my mapping or configuration. Any help is greatly appreciated.
After struggling with this problem for awhile longer I learned that I was configuring my Id properties in the wrong class.
Originally I was configuring orderId and memberId in the Order class
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Id
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
However, I learned that if you are using an IdClass OR EmbeddedId that you must make the appropriate field annotations for your Id columns in the ID Class.
#Entity
#Table(name="ORDERS")
#IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{
#Id
private Integer orderId;
#Id
private Integer memberId;
}
public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="ORDERS_ID", nullable=false)
private Integer orderId;
#Column(name="MEMBER_ID", nullable=false)
private Integer memberId;
}
With this change I was able to return the expected results with my test.
I had same kind of problem. I had to create backend on existing database, which had some tables without primary keys and with nullable columns (I know!!). This means that Hibernate entities for these kind of tables would have composite primary key consisting of all of the table columns.
When I queried the table rows through JpaRepository's findAll method, I would get a list of results where some of the results would be null values while others would be ok.
I finally found out that if some field of the composite key is null, it would render whole row as null result.
OrderPK defines a default constructor and 2 properties (orderId, memberId) along with their get/set methods.
I'm not sure this explains everything but... that's not enough. From the JPA 1.0 specification:
2.1.4 Primary Keys and Entity Identity
...
The following rules apply for composite primary keys.
The primary key class must be public and must have a public no-arg constructor.
If property-based access is used, the properties of the primary key class must be public or protected.
The primary key class must be serializable.
The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped.
A composite primary key must either be represented and mapped as an embeddable class (see Section 9.1.14, "EmbeddedId Annotation") or must be represented and mapped to multiple fields or properties of the entity class (see Section 9.1.15, "IdClass Annotation").
If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key fields or properties in the primary key class and those of the entity class must correspond and their types must be the same.
Could you please fix your OrderPK class to comply with the specification and try again?
As a side note, I wonder why you are using the loadTimeWeaver in your Spring configuration (since you're using Hibernate).
Update: I can't reproduce the problem... works as expected for me.

Categories

Resources