Hi I am getting hard time in saving one of the manytomany mapping entity due to an auditing issue.
We are using spring data with base entity to manage the audit information.
It all works fine but for one manytomany mapping I am getting sql exception
Field 'createdon' doesn't have a default value.
Please find below my config
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import com.minda.iconnect.cache.CacheableEntity;
/**
* Simple JavaBean domain object with an id property. Used as a base class for objects needing this property.
*
*/
#SuppressWarnings("serial")
#MappedSuperclass
public class BaseEntity implements TimeStampedAuditable, Serializable, CacheableEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#Column(name = "createdon", nullable = false)
protected Date createdOn;
#Column(name = "updatedon")
protected Date updatedOn;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public boolean isNew() {
return (this.id == null);
}
#Override
public Date getCreatedOn() {
return createdOn;
}
#Override
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
#Override
public Date getUpdatedOn() {
return updatedOn;
}
#Override
public void setUpdatedOn(Date updatedOn) {
this.updatedOn = updatedOn;
}
#Override
public String getCacheKey() {
return getId() == null ? "null" : String.valueOf(getId());
}
}
Below is mentioned sql for table creation:
create table role (
id bigint primary key not null auto_increment,
name varchar(255) not null,
role_code varchar(31) not null,
createdon datetime not null,
updatedon datetime
) engine=innodb;
create table user (
id bigint primary key not null auto_increment,
username varchar(255),
firstname varchar(255) not null,
lastname varchar(255) not null,
email varchar(255) not null,
createdon datetime not null,
updatedon datetime
) engine=innodb;
create table user_role (
id bigint primary key not null auto_increment,
roles_id bigint not null references role(id),
user_id bigint not null references user(id),
createdon datetime not null,
updatedon datetime
) engine=innodb;
I am using below code to save a user with an already existing role:
#Transactional
#Override
public User createUser(User user) {
Role role = roleService.getRoleByCode("Some Role");
List<Role> roles = new ArrayList<Role>();
roles.add(role);
user.setRoles(roles);
User savedUser = userRepository.save(user);
}
while saving I get sql error that the createdon field does not have a default value.
Since the relation is a self managed entity by jpa, I am confused that what needs to be done to make it audit-able.
Thanks,
Rohit Mishra
you are getting that exception because of the nullable = false, You can use a Jpa Entity Listener,
#PrePersist
protected void persistCreatedOn(){
this.createdOn = Calendar.getInstance().getTime();
}
this way JPA will set the createdOn just before persisting your Entity.
After analyzing the problem and spending some time we realized that the relations should not extend base entity and they need not to be audited the parent entity gives the information required. Its the job of the underneath orm to take care of the the relations.
Related
Class definition
#Entity
#Table(name = "t_domain")
public class Domain(){
#Id
String id;
String fieldA;
String fieldB;
String fieldC;
List<String> operations;
}
Table definition
CREATE TABLE `t_domain` (
`id` varchar(38) ,
`fieldA` varchar(255) ,
`fieldB` varchar(255) ,
`fieldC` varchar(255) ,
`operations` varchar(255) ,
PRIMARY KEY (`id`)
)
JSON
{"id":"1",
"fieldA":"a",
"fieldB":"b",
"fieldC":"c",
"operations":["a","b"]}
From this page['https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection'],said The ElementCollection values are always stored in a separate table..
In jpa 2.0,#ElementCollection is a way to save collection,but it seen to need a new table to store collection value.
Question:
I dont want to create any new table like domain_operation or another_table_name defind in #CollectionTable(name="another_table_name").
I want to save the json to mysql in only one row.
I'm using Hibernate 4.3.11
Ignore the below, it refers to original version of the question
#ElementCollection
#CollectionTable(
name="t_domain",
joinColumns=#JoinColumn(name = "id", referencedColumnName = "id")
)
#Column(name="operations")
Without specifying table name, Hibernate uses entityName_collectionName, which in your case gives domain_operation
Have you try using manually a serializator?
String operations;
#Transient
private ObjectMapper mapper;
public void setOperationsList(List<String> in) throws JsonProcessingException {
operations = mapper.writeValueAsString(in);
}
public List<String> getOperationsList() throws IOException {
return mapper.readValue(operations, new TypeReference<List<String>>(){});
}
}
I’m using JPA and trying to figure out how to create a unique constraint between a primary key column in one table and a non-primary key column in another. I have two tables:
Customer (
id character varying(32) NOT NULL,
customer_name character varying(50)
)
and
Account (
id character varying(32) NOT NULL,
account_name character varying(50)
)
There is a unidirectional, one-to-many relationship between Customer and Account with a constraint that Account.account_name is unique per Customer. The code looks like so:
#Entity
public class Customer {
#Id
#Column(length=32)
private String id;
#Column(unique=true, length=50)
private String customer_name;
#OneToMany
private List<Account> accounts;
...
}
and
#Entity
public class Account {
#Id
#Column(length=32)
private String id;
#Column(length=50)
private String account_name;
...
}
A join table is created by default:
CUSTOMER_ACCOUNTS (
customer_id character varying(32),
accounts_id character varying(32)
)
How do I create the unique constraint to ensure an Account.account_name is unique per Customer?
Add a JoinColumn annotation to the Customer class::
#Entity
public class Customer {
#Id
#Column(length=32)
private String id;
#Column(unique=true, length=50)
private String customer_name;
#OneToMany(mappedBy="customer")
#JoinColumn(name="CUSTOMER_ID", referencedColumnName="ID")
private List<Account> accounts;
...
}
Referenced column name points to the primary key of the Customer table, while name points to the foreign key field of the Account table.
You should also add a CUSTOMER_ID field and a composite unique constraint to the account entity:
#Entity
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"CUSTOMER_ID", "ACCOUNT_NAME"})
)
public class Account {
#Id
#Column(length=32)
private String id;
#Column(length=50)
private String account_name;
#Column(name="CUSTOMER_ID")
private String customer_id;
...
}
I do have a DB table with unidirectional trees. Leafs of these trees can have several children/parents.
Cycles are restricted.
Here is my DB table definition:
CREATE MULTISET TABLE WORKFLOW_SEQ_REL ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL,
REL_WORKFLOW_SEQ_ID INTEGER NOT NULL,
JOB_ID BIGINT)
PRIMARY INDEX ( WORKFLOW_SEQ_ID );
As you can see it doesn't have a primary key right now. But it would appear later :) Really PK is: JOB_ID+PARENT_ID+CHILD_ID.
The idea is:
REL_WORKFLOW_SEQ_ID = PARENT
WORKFLOW_SEQ_ID = CHILD
JOB_ID = TREE_IDENTIFICATOR (a determinant to separate different trees stored
in one table).
I'm trying to declare a JPA entity:
#Entity
#Table(name="WORKFLOW_SEQ_REL")
public class EtlWorkflowSeqNode {
#EmbeddedId
public EtlWorkflowSeqNodeId etlWorkflowSeqNodeId;
//#Column(name="JOB_ID")
//public Integer jobId;
#Embeddable
class EtlWorkflowSeqNodeId{
#Column(name="JOB_ID")
public Integer jobId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="REL_WORKFLOW_SEQ_ID")
//EtlWorkflowSeq.id = PK of EtlWorkflowSeq entity
public EtlWorkflowSeq parent;
#OneToMany(fetch=FetchType.EAGER /*, mappedBy="parent"*/)
#JoinColumn(name="WORKFLOW_SEQ_ID")
public Set<EtlWorkflowSeq> children;
}
}
And I gen an error:
Caused by: org.hibernate.AnnotationException: A Foreign key refering models.EtlWorkflowSeqNode from models.EtlWorkflowSeq has the wrong number of column.
should be 2
Here is EtlWorkflowSeq entity:
#Entity
#Table(name="WORKFLOW_SEQ")
public class EtlWorkflowSeq {
#Id
#Column(name="WORKFLOW_SEQ_ID")
public Integer id;
#OneToOne(fetch=FetchType.EAGER)
#JoinColumn(name="WORKFLOW_ID")
public EtlWorkflow etlWorkflow;
}
What do I do wrong?
UPD:
Here are table definitions:
--a bad design. PK should be: WORKFLOW_SEQ_ID + REL_WORKFLOW_SEQ_ID + JOB_ID
CREATE MULTISET TABLE WORKFLOW_SEQ_REL ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL, --a ref to child
REL_WORKFLOW_SEQ_ID INTEGER NOT NULL, -- a ref to parent
START_TYPE_ID SMALLINT NOT NULL, -- a type of connection
DISABLE_START_TYPE_ID SMALLINT, -- other type of connection
JOB_ID BIGINT) -- a tree determinant,
PRIMARY INDEX ( WORKFLOW_SEQ_ID );
CREATE MULTISET TABLE WORKFLOW_SEQ ,NO FALLBACK ,
NO BEFORE JOURNAL,
NO AFTER JOURNAL,
CHECKSUM = DEFAULT,
DEFAULT MERGEBLOCKRATIO
(
WORKFLOW_SEQ_ID INTEGER NOT NULL, -- an id
WORKFLOW_ID BIGINT NOT NULL, -- a ref to original workflow, not interesting
IS_NAME VARCHAR(255) CHARACTER SET UNICODE NOT CASESPECIFIC, -- some name
INFO_SYSTEM_INST_CD VARCHAR(255) CHARACTER SET UNICODE NOT CASESPECIFIC, -- other name
DISABLE BYTEINT) -- so garbage
UNIQUE PRIMARY INDEX ( WORKFLOW_SEQ_ID ); -- it should also be a PK
The Idea is that several trees are stored in WORKFLOW_SEQ_REL
JOB_ID is a determinant for trees.
WORKFLOW_SEQ_ID, REL_WORKFLOW_SEQ_ID refer some cutomized template from REL_WORKFLOW_SEQ table.
I cannot help noticing that there is an inconsistency in your question.
You first state that:
Leafs of these trees can have several children/parents.
This in my believe makes the relationship between leafs many to many.
As I make of your question that EtlWorkflowSeq represent leafs, I think EtlWorkflowSeqNode represents the relationship between EtlWorkflowSeq objects?
However, the nodes point to one parent and many children.
You can use something like this to create something similar:
#Entity
#Table(name="WORKFLOW_SEQ")
public class EtlWorkflowSeq
{
#Id
#GeneratedValue
#Column(name="WORKFLOW_SEQ_ID")
public Integer id;
#ManyToOne
#JoinColumn(name="WORKFLOW_ID")
public EtlWorkflow etlWorkflow;
#ManyToMany
#JoinTable(name = "WORKFLOW_SEQ_REL")
private Set<EtlWorkflowSeq> children;
#ManyToMany(mappedBy = "children")
private Set<EtlWorkflowSeq> parents;
#ManyToOne
#JoinColumn(name = "JOB_ID", referencedColumnName = "id")
private Job job;
}
This would make EtlWorkflowSeqNode and EtlWorkflowSeqNodeId obsolete.
I also would like to state that when using an #Embeddable you should only use base types in them. Using other than base types is not possible/causes problems/is not standard (correct me if I'm wrong).
If you would like to use foreign keys in a composite primary key you can use this:
#Entity
public class Foo
{
#Id
private Long id;
}
#Entity
public class Bar
{
#EmbeddedId
private BarPK key;
#MapsId(value = "fooId")
#ManyToOne
#JoinColumns({
#JoinColumn(name = "foo_id", referencedColumnName = "id")
})
private Foo foo;
}
#Embeddable
public class BarPK
{
#Column(name = "id")
private Long id;
#Column(name = "foo_id")
private Long fooId;
}
We are new to JPA and trying to setup a very simple one to many relationship where a pojo called Message can have a list of integer group id's defined by a join table called GROUP_ASSOC. Here is the DDL:
CREATE TABLE "APP"."MESSAGE" (
"MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
);
ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");
CREATE TABLE "APP"."GROUP_ASSOC" (
"GROUP_ID" INTEGER NOT NULL,
"MESSAGE_ID" INTEGER NOT NULL
);
ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");
ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_FK" FOREIGN KEY ("MESSAGE_ID")
REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");
Here is the pojo:
#Entity
#Table(name = "MESSAGE")
public class Message {
#Id
#Column(name = "MESSAGE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int messageId;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
private List groupIds;
public int getMessageId() {
return messageId;
}
public void setMessageId(int messageId) {
this.messageId = messageId;
}
public List getGroupIds() {
return groupIds;
}
public void setGroupIds(List groupIds) {
this.groupIds = groupIds;
}
}
I know this is wrong as there is no #Column mapping to GROUP_ASSOC.GROUP_ID for the groupIds property, but hopefully this illustrates what we are trying to do. When we run the following test code we get <openjpa-1.2.3-SNAPSHOT-r422266:907835 fatal user error> org.apache.openjpa.util.MetaDataException: The type of field "pojo.Message.groupIds" isn't supported by declared persistence strategy "OneToMany". Please choose a different strategy.
Message msg = new Message();
List groups = new ArrayList();
groups.add(101);
groups.add(102);
EntityManager em = Persistence.createEntityManagerFactory("TestDBWeb").createEntityManager();
em.getTransaction().begin();
em.persist(msg);
em.getTransaction().commit();
Help!
When you are working with JPA, you should think Object and relations between Objects and you should map your Object model, not ids, to your relational model (it is possible to map a List of basic values with #ElementCollection in JPA 2.0 though but what I said just before still applies).
Here, (assuming this really is a one-to-many relation between Message and GroupAssoc and not a many-to-many relation between Message and Group entities) you should have something like this:
#Entity
#Table(name = "MESSAGE")
public class Message implements Serializable {
#Id
#Column(name = "MESSAGE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long messageId;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
private List<GroupAssoc> groupAssocs = new ArrayList<GroupAssoc>();
public Long getMessageId() {
return messageId;
}
public void setMessageId(Long messageId) {
this.messageId = messageId;
}
public List<GroupAssoc> getGroupAssocs() {
return groupAssocs;
}
public void setGroupAssocs(List<GroupAssoc> groupAssocs) {
this.groupAssocs = groupAssocs;
}
// equals() and hashCode()
}
And another entity for GroupAssoc.
PS: Your DDL really looks like a (M:N) relation between MESSAGE and GROUP (or I don't understand the PK constraint of GROUP_ASSOC) but you didn't show any FK constraint on GROUP_ID so I'm not 100% sure. But if that's the case, then you should use an #ManyToMany instead of #OneToMany.
We have a pojo that needs to have a list of integers. As an example, I've created a Message pojo and would like to associate a list of groupIds (these ids need to be queried and displayed in the UI). So ideally, we would like to be able to do something like this:
Message msg = em.find(Message.class, 101);
List<Integer> groupIds = msg.getGroupIds();
I was under the impression that this would require only one pojo with JPA, but according to the discussion here, I need to create a second pojo because JPA works in terms of objects instead of primitive types.
From that discussion I've tried the following example code, but I get the error openjpa-1.2.3-SNAPSHOT-r422266:907835 fatal user error: org.apache.openjpa.util.MetaDataException: The type of field "pojo.Group.messageId" isn't supported by declared persistence strategy "ManyToOne". Please choose a different strategy.
DDL:
CREATE TABLE "APP"."MESSAGE" (
"MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
"AUTHOR" CHAR(20) NOT NULL
);
ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");
CREATE TABLE "APP"."GROUP_ASSOC" (
"GROUP_ID" INTEGER NOT NULL,
"MESSAGE_ID" INTEGER NOT NULL
);
ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");
ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_FK" FOREIGN KEY ("MESSAGE_ID")
REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");
POJOs:
#Entity
#Table(name = "MESSAGE")
public class Message {
#Id
#Column(name = "MESSAGE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long messageId;
#OneToMany
private List<Group> groups = new ArrayList<Group>();
#Column(name = "AUTHOR")
private String author;
// getters/setters ommitted
}
#Entity
#IdClass(pojo.Group.GroupKey.class)
#Table(name = "GROUP_ASSOC")
public class Group {
#Id
#Column(name = "GROUP_ID")
private Long groupId;
#Id
#Column(name = "MESSAGE_ID")
#ManyToOne
private Long messageId;
public static class GroupKey {
public Long groupId;
public Long messageId;
public boolean equals(Object obj) {
if(obj == this) return true;
if(!(obj instanceof Group)) return false;
Group g = (Group) obj;
return g.getGroupId() == groupId && g.getMessageId() == messageId;
}
public int hashCode() {
return ((groupId == null) ? 0 : groupId.hashCode())
^ ((messageId == null) ? 0 : messageId.hashCode());
}
}
// getters/setters ommitted
}
Test Code:
EntityManager em = Persistence.createEntityManagerFactory("JPATest").createEntityManager();
em.getTransaction().begin();
Message msg = new Message();
msg.setAuthor("Paul");
em.persist(msg);
List<Group> groups = new ArrayList<Group>();
Group g1 = new Group();
g1.setMessageId(msg.getMessageId());
Group g2 = new Group();
g2.setMessageId(msg.getMessageId());
msg.setGroups(groups);
em.getTransaction().commit();
This all seems ridiculous -- 3 classes (if you include the GroupKey composite identity class) to model a list of integers -- isn't there a more elegant solution?
This is an old topic but things have changed since OpenJPA2, now you can directly persist primitive types or String object. Use ElementCollection annotation to use simple one-to-many linking, no need to intermediate object or link tables. This is how most of us probably create SQL schemas.
#Entity #Table(name="user") #Access(AccessType.FIELD)
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private long id; // primary key (autogen surrogate)
private String name;
// ElementCollection provides simple OneToMany linking.
// joinColumn.name=foreign key in child table. Column.name=value in child table
#ElementCollection(fetch=FetchType.LAZY)
#CollectionTable(name="user_role", joinColumns={#JoinColumn(name="user_id")})
#Column(name="role")
private List<String> roles;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name=name; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles=roles; }
}
- - -
CREATE TABLE user (
id bigint NOT NULL auto_increment,
name varchar(64) NOT NULL default '',
PRIMARY KEY (id),
UNIQUE KEY USERNAME (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE user_role (
user_id bigint NOT NULL,
role varchar(64) NOT NULL default '',
PRIMARY KEY (user_id, role)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
I really think that what you have is in fact a many-to-many association between two Entities (let's call them Message and Group).
The DDL to represent this would be:
CREATE TABLE "APP"."MESSAGE" (
"MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
"AUTHOR" CHAR(20) NOT NULL
);
ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");
CREATE TABLE "APP"."GROUP" (
"GROUP_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
);
ALTER TABLE "APP"."GROUP" ADD CONSTRAINT "GROUP_PK" PRIMARY KEY ("GROUP_ID");
CREATE TABLE "APP"."MESSAGE_GROUP" (
"GROUP_ID" INTEGER NOT NULL,
"MESSAGE_ID" INTEGER NOT NULL
);
ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");
ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK1" FOREIGN KEY ("MESSAGE_ID")
REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");
ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK2" FOREIGN KEY ("GROUP_ID")
REFERENCES "APP"."MESSAGE" ("GROUP_ID");
And the annotated classes:
#Entity
public class Message {
#Id
#Column(name = "MESSAGE_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long messageId;
#ManyToMany
#JoinTable(
name = "MESSAGE_GROUP",
joinColumns = #JoinColumn(name = "MESSAGE_ID"),
inverseJoinColumns = #JoinColumn(name = "GROUP_ID")
)
private List<Group> groups = new ArrayList<Group>();
private String author;
//...
}
#Entity
public class Group {
#Id
#GeneratedValue
#Column(name = "GROUP_ID")
private Long groupId;
#ManyToMany(mappedBy = "groups")
private List<Message> messages = new ArrayList<Message>();
//...
}
I'm not sure you need a bi-directional association though. But you definitely need to start to think object if you want to use JPA (in you're example, you're still setting ids, you should set Entities). Or maybe JPA is not what you need.
isn't there a more elegant solution?
I'm not sure "elegant" is appropriate but JPA 2.0 defines an ElementCollection mapping (as I said in my previous answer):
It is meant to handle several non-standard relationship mappings. An ElementCollection can be used to define a one-to-many relationship to an Embeddable object, or a Basic value (such as a collection of Strings).
But that's in JPA 2.0. In JPA 1.0, you would have to use a provider specific equivalent, if your provider does offer such an extension. It appears that OpenJPA does with #PersistentCollection.
Based on your schema you have a ManyToOne relationship between Group and Message. Which means that a single Message can belong to multiple groups, but each group can have a single message.
The entities would look something like this.
#Entity
#Table(name = "GROUP_ASSOC")
public class Group {
#Id
#Column(name="GROUP_ID")
private int id;
#ManyToOne
#Column(name="MESSAGE_ID")
#ForeignKey
private Message message;
// . . .
}
#Entity
public class Message {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "MESSAGE_ID")
private int id;
#Column(length=20)
private String author;
#OneToMany(mappedBy="message")
private Collection<Group> groups;
}
There's no need for an IDClass in your app (you only need one if your ID is contains multiple columns).
To get the groupIds for a given message you could write a query like this one
Query q = em.createQuery("Select g.id from Group g where g.message.id = :messageId");
q.setParameter("messageId", 1);
List results = q.getResultList();
Or just iterate over Message.getGroups() :
Message m = em.find(Message.class, 1);
for(Group g : m.getGroups()) {
// create a list, process the group whatever fits.
}