The problem with JPA Composite primary keys and CascadeType.ALL

Following my previous blog here, I encountered another issue with the use of composite primary keys.

With JPA, we can specify the cascade of relationship to be CascadeType.ALL, so that when we persist the CUSTOMER, all of its PRODUCT are persisted too.

So, the code will be modified as follows:

CUSTOMER

package com.wordpress.dwuysan;

// imports omitted

@Entity
@Table(name = "customer")
public class Customer implements Serializable {
    @Id
    @Column(name = "customer_id")
    private String customerId;

    @OneToMany(
        fetch = FetchType.EAGER,
        mappedBy = "customer",
        cascade = CascadeType.ALL)
    private Set<Product> products = new HashSet<Product>();

    /* the rest of the code omitted */
}

During unit testing however, when executing the following lines:

    this.entityManager.persist(customer); // customer with child products here
    final Query query = this.entityManager.createQuery("FROM Customer cust");
    return query.getResultList(); // error here

And the error is:

javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
	at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:637)
	at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:74)
	at com.wordpress.dwuysan.CustomerServiceBean(CustomerServiceBean.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)

SO, for now, I think I will use another ID for the PRODUCT. Would need to look for the underlying reason.

@JoinColumn is part of the composite primary keys

Whilst it is not very common (hmmm … , come to think of it, it may be quite common), there are cases where one would require the use of composite primary keys in JPA, but at the same time, one of the key is actually a foreign key to another parent table.

Let’s just say for example there are table CUSTOMER and PRODUCT (Smith 2007), in this case the PRODUCT uses composite primary key of PRIMARY KEY (customer_id, product_id).

In JPA, they are mapped as follows:

CUSTOMER

package com.wordpress.dwuysan;

// imports omitted

@Entity
@Table(name = "customer")
public class Customer implements Serializable {
    @Id
    @Column(name = "customer_id")
    private String customerId;

    @OneToMany(
        fetch = FetchType.EAGER,
        mappedBy = "customer")
    private Set<Product> products = new HashSet<Product>();

    /* the rest of the code omitted */
}

PRODUCTPK

package com.wordpress.dwuysan;

// imports omitted

@Embeddable
public class ProductPK implements Serializable {
    private String productId;
    private String customerId;

    /* the rest of the code omitted */
}

PRODUCT

package com.wordpress.dwuysan;

// imports omitted

@Entity
@Table(name = "product")
public class Product implements Serializable {

    @AttributeOverrides({
        @AttributeOverride(
            name = "customerId",
            column = @Column(name = "customer_id")),
        @AttributeOverride(
            name = "productId",
            column = @Column(name = "productId"))
    })
    private ProductPK productPK;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "customer_id")
    private Customer customer;
}

However, on compiling, I got the following error from JUnit:

Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.wordpress.dwuysan.Product column: customer_id (should be mapped with insert="false" update="false")
	at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:652)
	at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:674)
	at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:696)
	at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:450)
	at org.hibernate.mapping.RootClass.validate(RootClass.java:192)
	at org.hibernate.cfg.Configuration.validate(Configuration.java:1108)

So, to resolve this, we need to modify the Product class to:

PRODUCT

package com.wordpress.dwuysan;

// imports omitted

@Entity
@Table(name = "product")
public class Product implements Serializable {

    @AttributeOverrides({
        @AttributeOverride(
            name = "customerId",
            column = @Column(name = "customer_id")),
        @AttributeOverride(
            name = "productId",
            column = @Column(name = "productId"))
    })
    private ProductPK productPK;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "customer_id", insertable = false, updatable = false)
    private Customer customer;
}

Reference:
Smith, J, 2007, ‘Composite Primary Keys’, accessed 22 February 2012.

org.hibernate.type.SerializationException and java.io.StreamCorruptedException: invalid stream header

Today, I encountered the following error when developing a few JPA entities.

Originally thrown as : java.io.StreamCorruptedException: invalid stream header: 52657175
	at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:782)
	at java.io.ObjectInputStream.(ObjectInputStream.java:279)
	at org.hibernate.util.SerializationHelper$CustomObjectInputStream.(SerializationHelper.java:252)
	at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:209)
Rethrown as : org.hibernate.type.SerializationException: could not deserialize
	at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:217)
	at org.hibernate.util.SerializationHelper.deserialize(SerializationHelper.java:240)
	at org.hibernate.type.SerializableType.fromBytes(SerializableType.java:82)
	at org.hibernate.type.SerializableType.get(SerializableType.java:39)
	at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
	at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
	at org.hibernate.loader.Loader.getKeyFromResultSet(Loader.java:1097)
	at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:565)
	at org.hibernate.loader.Loader.doQuery(Loader.java:701)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
	at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
	at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:37)
	at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:566)
	at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:64)
	at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:87)
	at org.hibernate.collection.AbstractPersistentCollection.readElementExistence(AbstractPersistentCollection.java:143)
	at org.hibernate.collection.PersistentSet.contains(PersistentSet.java:153)
	at com.yarris.consol.messagelistener.RequeueToIntegrationHandler.isAppropriateHandler(RequeueToIntegrationHandler.java:69)
	at com.yarris.consol.messagelistener.OrderBAMMessageListener.onMessage(OrderBAMMessageListener.java:90)
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:561)
	at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:498)
	at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:467)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:325)
	at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:243)
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1058)
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1050)
	at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:947)
	at java.lang.Thread.run(Thread.java:662)

Basically, I have two JPA entities, and they have @ManyToMany relationship.

First JPA entity:

public class Person implements Serializable {
    @Id
    @Column(name = "person_no")
    private Long personNo;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "person_specialisation",
        joinColumns = 
            @JoinColumn(name = "person_no",
                referencedColumnName = "person_no"),
        inverseJoinColumns = 
            @JoinColumn(name = "specialisation_type", 
                referencedColumnName = "specialisation_type"))
    private Set<Specialisation> specs = new HashSet<Specialisation>();

    // ... rest of the code omitted
}

Second JPA entity:

public class Specialisation implements Serializable {
    @Id
    @Column(name = "specialisation_type")
    @Enumerated(EnumType.STRING)
    private SpecialisationType specialisationType;

    @ManyToMany(
        fetch = FetchType.LAZY,
        mappedBy = "specialisations")
    private Set<Person> persons = new HashSet<Person>

    // ... rest of the code omitted
}

SO, basically the finding is that enumeration cannot be used as Id. Some posts also suggest this cannot be done.

References:
Hibernate, 2010, ‘JPA Support of Enum as Primary Key’, accessed on 09 February 2012.
Bozho, 2010, ‘Using enum as id’, accessed on 09 February 2012.

Cannot MODIFY CONSTRAINT in Oracle

I just notice that in Oracle, one cannot really MODIFY constraints. Instead, should DROP and ADD.

For example, the following line:

ALTER TABLE work_order MODIFY CONSTRAINT status_check CHECK (business_object IN ('Open', 'Closed', 'Hold'));

results in:

Error report:
SQL Error: ORA-00933: SQL command not properly ended
00933. 00000 -  "SQL command not properly ended"
*Cause:
*Action:

h:selectManyListbox. javax.faces.SelectItem and the fact the selected items always returned as a List<String>

Just today I discovered that by default, whatever selected in a multi listbox, h:selectManyListBox always returns List<String>.

Imagine the java class looks like this:

public class Bean implements Serializable {
    public List<SelectedLongs> selectedLongs;
    // accessor and mutator of property 'selectedLongs'

    public List<SelectItem> getLongOptions() {
        final List<SelectItem> options = new ArrayList<SelectItem>();
        options.add(new SelectItem(1l, "1");
        options.add(new SelectItem(2l, "2");
        options.add(new SelectItem(3l, "3");
    }
}

And, the following XHTML for JSF display:

<h:selectManyListbox size="5" value="#{bean.selectedLongs}">
    <f:selectItems value="#{bean.longOptions}" />
</h:selectManyListbox>

Surprisingly, what received during submit is not a List<Long>, but List<String>.

In order to fix this, remember to add converter="javax.faces.Long" as shown below:

<h:selectManyListbox size="5" value="#{bean.selectedLongs}" converter="javax.faces.Long">
    <f:selectItems value="#{bean.longOptions}" />
</h:selectManyListbox>

Reference:
BalusC, 2010, ‘JSF 2.0 use enum in selectMany menu’, accessed 03 February 2012