Eduardo Rodrigues Eduardo Rodrigues - 5 months ago 63
Java Question

Hibernate 4 mapping composition of objects

An existing database contains the following table

CREATE TABLE `memberships` (
`group_id` int(11) NOT NULL default '0',
`user_id` int(11) NOT NULL default '0',
`status` int(11) default NULL,
PRIMARY KEY (`group_id`,`user_id`)
)


Which maps to basic id/name Group and User tables

How do I map that on Hibernate?

public class Memberships {
private User user;
private Group group;
private Integer status;

... getters/setters

}


I'd like do have something like above

I have been trying with
@IdClass
and
@Embeddable
but no success

Any thoughts?

Java 8, Hibernate 4

UPDATES: working class on the mapping

@Entity
@Table(name = "memberships")
@Getter
@Setter
@EqualsAndHashCode
@ToString
@Accessors(chain = true)
@IdClass(Memberships.MembershipsPK.class)
public class Memberships implements Serializable {

@Id
@ManyToOne
@JoinColumn(name = "user_id")
private User user;

@Id
@ManyToOne
@JoinColumn(name = "group_id")
private Group group;

@Column(name = "status")
private Integer status;

@Getter
@Setter
@EqualsAndHashCode
@ToString
public static class MembershipsPK implements Serializable {

@Id
@Column(name = "user_id")
private long user;

@Id
@Column(name = "group_id")
private long group;


}

}


I'm using Lombok Project for getter & setter

JPA Repository:

public interface MembershipsRepository extends JpaRepository<Memberships, Memberships.MembershipsPK>, RevisionRepository<Memberships, Memberships.MembershipsPK, Integer> {
}

User user = userBo.findOne(1l);
Group group = new Group().setApplication(application).setName("teste");
groupService.save(group);

Memberships memberships = new Memberships().setGroup(group).setUser(user).setStatus(1);

membershipsService.save(memberships);


EXCEPTION:

Exception in thread "main" org.springframework.orm.jpa.JpaSystemException: could not set a field value by reflection setter of com.lotjm.domain.Memberships$MembershipsPK.user; nested exception is org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.lotjm.domain.Memberships$MembershipsPK.user
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:314)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:436)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy78.save(Unknown Source)
at com.lotjm.service.impl.GenericRepositoryServiceImpl.save(GenericRepositoryServiceImpl.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy79.save(Unknown Source)
at com.lotjm.util.HibernateReadingTesting.main(HibernateReadingTesting.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.lotjm.domain.Memberships$MembershipsPK.user
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:134)
at org.hibernate.tuple.component.AbstractComponentTuplizer.setPropertyValues(AbstractComponentTuplizer.java:93)
at org.hibernate.tuple.component.PojoComponentTuplizer.setPropertyValues(PojoComponentTuplizer.java:116)
at org.hibernate.type.ComponentType.setPropertyValues(ComponentType.java:439)
at org.hibernate.tuple.entity.AbstractEntityTuplizer$NormalMappedIdentifierValueMarshaller.getIdentifier(AbstractEntityTuplizer.java:437)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:342)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4746)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:164)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:294)
at com.sun.proxy.$Proxy62.merge(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 27 more
Caused by: java.lang.IllegalArgumentException: Can not set long field com.lotjm.domain.Memberships$MembershipsPK.user to com.lotjm.domain.User
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeLongFieldAccessorImpl.set(UnsafeLongFieldAccessorImpl.java:102)
at java.lang.reflect.Field.set(Field.java:764)
at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:122)
... 61 more


ALTERNATIVE SOLUTION:

@Entity
@Table(name = "memberships")
@Getter
@Setter
@EqualsAndHashCode
@ToString
@Accessors(chain = true)
public class Memberships implements Serializable {

@EmbeddedId
@JoinColumns({
@JoinColumn(name = "user_id", referencedColumnName = "user_id"),
@JoinColumn(name = "group_id", referencedColumnName = "group_id")
})
private MembershipsPK id = new MembershipsPK();

public Memberships setGroup(Group group) {
id.setGroup(group);
return this;
}

public Memberships setUser(User user) {
id.setUser(user);
return this;
}


@Column(name = "status")
private Integer status;

@Embeddable
@Getter
@Setter
@EqualsAndHashCode
public static class MembershipsPK implements Serializable {
@ManyToOne(targetEntity = User.class)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(targetEntity = Group.class)
@JoinColumn(name = "group_id")
private Group group;
}

}

Answer
@Entity
@Table(name = "memberships")
public class Memberships implements Serializable {

  @Id
  @ManyToOne
  @JoinColumn(name = "user_id")
  private User user;

  @Id
  @ManyToOne
  @JoinColumn(name = "group_id")
  private Group group;

  @Column(name = "status")
  private Integer status;

}

This mapping generates this SQL for H2 database ( I use Long for ids)

create table memberships (
        status integer,
        user_id bigint not null,
        group_id bigint not null,
        primary key (user_id, group_id)
)

Update

To solve the issue:

org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.lotjm.domain.Memberships$MembershipsPK.user

just remove @Id and @Column annotations from the fields of MembershipsPK.

@Getter
@Setter
@EqualsAndHashCode
@ToString
public static class MembershipsPK implements Serializable {

    private long user;

    private long group;

}
Comments