Complex @Id with JPAContainer

Hi

I am quite new to Vaadin and JPAContainer (using EclipseLink) and I am having a problem I could not resolve. I will start explaining the entities I have and its relations:

  • User
  • Company
  • CompanyAssignment

User has a list of CompanyAssignment, and CompanyAssignment has a field “role”, “company” and user. Here are entities:


@Entity
public class User {

    @NotNull
    @Column(unique = true)
    @Size(min = 4, max = 16)
    private String username;

    @NotNull
    @Size(min = 4)
    private String password;

    private Boolean active;

    @NotNull
    @Size(max = 255)
    private String name;

    @OneToMany(mappedBy="user", cascade={CascadeType.MERGE}, orphanRemoval=true)
    private List<CompanyAssignment> company;

    //Getters and setters
}


@Entity
public class Company {

    @Size(max = 255)
    private String name;

    @OneToMany(mappedBy="company", cascade={CascadeType.MERGE})
    private List<CompanyAssignment> users;

    //Getters and setters
}

@Entity
@IdClass(CompanyAssignmentId.class)
public class CompanyAssignment {
	

    @Id
    @ManyToOne
    @JoinColumn(name="COMPANY_ID", insertable=true, updatable=false)
    private Company company;
    @Id
    @ManyToOne
    @JoinColumn(name="USER_ID", insertable=true, updatable=false)
    private User user;
    
    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(name="ROLE", insertable=true, updatable=true)
    private CompanyRoles role;

    //Getters and setters
}


//Not an Entity
public class CompanyAssignmentId implements Serializable {
	
	private Long company;
	private Long user;
	public Long getCompany() {
		return company;
	}
	public void setCompany(Long company) {
		this.company = company;
	}
	public Long getUser() {
		return user;
	}
	public void setUser(Long user) {
		this.user = user;
	}

}

So CompanyAssignment entity’s ID are composed by two PK.

Now I want to make an User administration page consisting on a User table and a Form. When the user clicks a row the Form appears with data to edit. The problem is User entity’s Company field representation. I am using a CustomField make of a Table with only two columns: one for the Company name (nested field “company.name”) and a ComboBox (using an inherited DefaultFieldFactory to display it).

In the User Table I am using a JPAContainerFactory.make(). When clicking a row I pass the EntityItem to the Form. In the form I call setItemDataSource to set the item and using a DefaultFieldFactory I create a CompanyAssignmentTable which extend CustomField.

In this CustomField I create a BeanItemContainer (or BeanContainer<CompanyAssignmentId,CompanyAssignment>) and override setPropertyDataSource method to add all beans to the container.

On the Form I have an apply button which calls Form.Commit() method.

All data displays correctly, but when clicking on Commit button in the Form I get an EclipseLink error unable to update primray key columns.


[EL Warning]
: 2012-02-02 11:20:51.814--UnitOfWork(1454176402)--Thread(Thread[http-8080-2,5,main]
)--Exception [EclipseLink-7251]
 (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [user]
 of class [com.myproject.domain.CompanyAssignment]
 is mapped to a primary key column in the database. Updates are not allowed.
2012-02-02 11:20:51,815 [http-8080-2]
 com.myproject.web.ui.panels.admin.FormUser$1.buttonClick(FormUser.java:94) ERROR  - Unable to save user
com.vaadin.data.Buffered$SourceException
	at com.vaadin.ui.Form.commit(Form.java:365)
	at com.myproject.web.ui.panels.admin.FormUser$1.buttonClick(FormUser.java:86)
	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 com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:512)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:164)
	at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1219)
	at com.vaadin.ui.Button.fireClick(Button.java:550)
	at com.vaadin.ui.Button.changeVariables(Button.java:217)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.changeVariables(AbstractCommunicationManager.java:1451)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariableBurst(AbstractCommunicationManager.java:1399)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1318)
	at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:763)
	at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:296)
	at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:501)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:291)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:662)
Caused by: com.vaadin.data.Buffered$SourceException
	at org.vaadin.addon.customfield.CustomField.commit(CustomField.java:186)
	at com.myproject.web.ui.panels.admin.CompanyAssignmentTable.commit(CompanyAssignmentTable.java:237)
	at com.vaadin.ui.Form.commit(Form.java:339)
	... 29 more
Caused by: com.vaadin.data.Property$ConversionException: javax.persistence.PersistenceException: Exception [EclipseLink-7251]
 (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [user]
 of class [com.myproject.domain.CompanyAssignment]
 is mapped to a primary key column in the database. Updates are not allowed.
	at com.vaadin.addon.jpacontainer.JPAContainerItem$ItemProperty.setValue(JPAContainerItem.java:289)
	at org.vaadin.addon.customfield.CustomField.commit(CustomField.java:180)
	... 31 more
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-7251]
 (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [user]
 of class [com.myproject.domain.CompanyAssignment]
 is mapped to a primary key column in the database. Updates are not allowed.
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:747)
	at com.vaadin.addon.jpacontainer.provider.MutableLocalEntityProvider$4.run(MutableLocalEntityProvider.java:208)
	at com.vaadin.addon.jpacontainer.provider.MutableLocalEntityProvider.runInTransaction(MutableLocalEntityProvider.java:117)
	at com.vaadin.addon.jpacontainer.provider.MutableLocalEntityProvider.updateEntityProperty(MutableLocalEntityProvider.java:194)
	at com.vaadin.addon.jpacontainer.provider.CachingMutableLocalEntityProvider.updateEntityProperty(CachingMutableLocalEntityProvider.java:185)
	at com.vaadin.addon.jpacontainer.JPAContainer.containerItemPropertyModified(JPAContainer.java:1186)
	at com.vaadin.addon.jpacontainer.JPAContainerItem$ItemProperty.setValue(JPAContainerItem.java:282)
	... 32 more
Caused by: Exception [EclipseLink-7251]
 (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The attribute [user]
 of class [com.myproject.domain.CompanyAssignment]
 is mapped to a primary key column in the database. Updates are not allowed.
	at org.eclipse.persistence.exceptions.ValidationException.primaryKeyUpdateDisallowed(ValidationException.java:2393)
	at org.eclipse.persistence.mappings.OneToOneMapping.writeFromObjectIntoRowWithChangeRecord(OneToOneMapping.java:1710)
	at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildRowForUpdateWithChangeSet(ObjectBuilder.java:1206)
	at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1126)
	at org.eclipse.persistence.queries.UpdateObjectQuery.executeCommitWithChangeSet(UpdateObjectQuery.java:84)
	at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:291)
	at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
	at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:808)
	at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:711)
	at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
	at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2842)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1521)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1503)
	at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1463)
	at org.eclipse.persistence.internal.sessions.CommitManager.commitChangedObjectsForClassWithChangeSet(CommitManager.java:265)
	at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:128)
	at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:3766)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1404)
	at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:616)
	at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1552)
	at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:427)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:744)
	... 38 more

So it seems JPAContainer is trying to update user field in CompanyAssignment, but I have not made any changes to it (only to role field). Is there any way to make user and company field in CompanyAssignment not be written to DB?

Ok, I will shorten the question, is there any way to make JPAContainer don’t update specific fields? Currently it seems is trying to update “user” and “company” fields, and as they are primary keys EclipseLink complains. I have tried using

  containerPermission.removeContainerProperty("user");
  containerPermission.removeContainerProperty("company");

since my table doesn’t update those values, but still the same error. Checked with debugger “containerPermission.getContainerPropertyIds()” and those properties are effectively removed. Any ideas how can I do this?

May be the whole approach is incorrect, any suggestions on how can I show this data?

Hi,

Vaadin Form has such an odd feature that it commits all fields although they were not modified. And fields in Vaadin do have isModified() method that it could check. But in your case it is probably better to hide those id fields in you form.

You should call form method “setVisibleItemProperties” without those property identifiers that you don’t want to modify. You could also modify field factory to return null for those properties that you don’t want to modify.

cheers,
matti