I have two 3 tables in my database:
group
----------
groupId PK
name
user_account
----------
userId PK
user_grouping
----------
groupId PK FK grouping(groupId -> groupId)
userId PK FK user_account(userId -> userId)
In my UserAccount Entity, I have the following line:
#JoinTable(name = "user_group", joinColumns = {
#JoinColumn(name = "userId", referencedColumnName = "userId")}, inverseJoinColumns = {
#JoinColumn(name = "groupId", referencedColumnName = "groupId")})
#ManyToMany
private List<Grouping> groupingList;
This is to show the relationship between all the tables. However, when I deploy, I get the following error:
SEVERE: Exception while preparing the app : Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [com.dv_model_ejb_1.0-SNAPSHOTPU] failed.
Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The #JoinColumns on the annotated element [field groupingList] from the entity class [class com.dv.model.entity.UserAccount] is incomplete. When the source entity class uses a composite primary key, a #JoinColumn must be specified for each join column using the #JoinColumns. Both the name and the referencedColumnName elements must be specified in each such #JoinColumn.
Local Exception Stack:
Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [com.dv_model_ejb_1.0-SNAPSHOTPU] failed.
Internal Exception: Exception [EclipseLink-7220] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The #JoinColumns on the annotated element [field groupingList] from the entity class [class com.dv.model.entity.UserAccount] is incomplete. When the source entity class uses a composite primary key, a #JoinColumn must be specified for each join column using the #JoinColumns. Both the name and the referencedColumnName elements must be specified in each such #JoinColumn.
at org.eclipse.persistence.exceptions.EntityManagerSetupException.predeployFailed(EntityManagerSetupException.java:221)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1402)
...
I am not sure exactly how to interpret this error message. I am assuming I do not have the table relationship correctly modeled in my entity. But I am not sure why. Before today, this was compiling fine. Can anyone provide assistance?
Description of tables is inconsistent (grouping vs group) and name of join table in entity mappings is not one of the table names. Because of these
inconsistencies I assume following table structure:
useraccount (userid PK)
grouping (groupdid PK, name)
user_grouping (userId PK, groupId PK)
- FK userId references to user_account.userid
- FK groupId references to grouping.groupId
One correct way to map this to two entities is following:
#Entity
public class UserAccount {
#Id int userId;
#JoinTable(name = "user_grouping", joinColumns = {
#JoinColumn(name = "userId", referencedColumnName = "userId")},
inverseJoinColumns = {
#JoinColumn(name = "groupId", referencedColumnName = "groupId")})
#ManyToMany
private List<Grouping> groupingList;
//get and set omitted.
}
#Entity
public class Grouping {
#Id int groupId;
String name;
//get,set, and optional inverse side of relationship omitted
}
Related
Using WildFly Full 17.0.1.Final (WildFly Core 9.0.2.Final) / EclipseLink 2.7.7 with MySQL server, I have few fields (of enum type, of Instant as well) in a parent entity (#MappedSuperclass) that I want to inherit in few sub-entities.
The problem I have is that the annotation #Enumerated(STRING), put in the parent entity fields, is not taken into account and EclipseLink is trying to persist the enums as integers instead of as strings.
The same issue with Instant type fields - the annotation #Convert(converter = InstantConverter.class) is not taken into account
When I move the fields to live in the child entities (not using inheritance), it works as expected.
Here is the code:
#MappedSuperclass
#Access(AccessType.PROPERTY)
public abstract class BaseEntity {
#Id
#Column(name = "ID", nullable = false)
private String id;
#Column(name = "STATUS", nullable = false)
#Enumerated(EnumType.STRING)
private StatusEnum status;
#Column(name = "TIMESTAMP", nullable = false)
#Convert(converter = InstantConverter.class)
private Instant timestamp;
// ... getters, setters
}
#Entity
#Table(name = "child1")
public class ChildEntity extends BaseEntity {
// other fields with getters and setters
}
The exceptions:
[EL Warning]: UnitOfWork(353321401)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value: '\xAC\xED\x00\x05sr\x00\x0Djava.time.Ser\x95]\x84\xBA\x1B"H\xB2\x0C\x00\x00xpw\x0D\x02\x00\x00\x00\x00aI\xD3\x86\x1B\xDD\xBCTx' for column 'TIMESTAMP' at row 1
[EL Warning]: UnitOfWork(597716366)--Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [FAILURE], of class [class java.lang.String], from mapping [org.eclipse.persistence.mappings.DirectToFieldMapping[status-->d.s.STATUS]] with descriptor [RelationalDescriptor(d.e.ChildEntity --> [DatabaseTable(...)])], could not be converted to [class java.lang.Integer].
Internal Exception: java.lang.NumberFormatException: For input string: "FAILURE"
Any idea how to resolve this problem?
Edit: There is no #Entity annotation on BaseEntity class as it is #MappedSuperclass. There is no table in DB behind it as well and this is the desired model. I just mention it, don't know if it is related to the issue.
I have this hierarchy
T1(
id (pk),
name (varchar)
)
T2(
id (pk),
t1_id (fk_t1),
number (int)
)
T3 (
id (pk),
t2_id (fk_t2),
time (datetime)
zone (tinyint),
name (varchar)
)
and this is my T3Entity
#Entity
#Table(name="T3, schema="", catalog="dbname")
public class T3Entity{
private int id;
private DateTime datetime;
private int zone;
private String name;
#Id
#Column(name="id", nullable=false, insertable=true, updatable=true)
//GETTER/SETTERS
#Basic
#Column(name="datetime", nullable=false, insertable=true, updatable=true)
//GETTER/SETTERS
#Basic
#Column(name="zone", nullable=false, insertable=true, updatable=true)
//GETTER/SETTERS
#Basic
#Column(name="name", nullable=false, insertable=true, updatable=true)
//GETTER/SETTERS
}
When I added this code in T3Entity class, I got an error
#ManyToOne(fetch=FetchType.Lazy)
#JoinColumn(name="T2_id", referencedColumn="T2_id")
private T2Entity t2Entity;
//getters-setters
I got this error
Internal Exception: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [PU] failed.
Internal Exception: Exception [EclipseLink-7161] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Entity class [class com.project.entity.T3Entity] has no primary key specified. It should define either an #Id, #EmbeddedId or an #IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.
Shouldn't this be
#JoinColumn(name="T2_id")
instead of
#JoinColumn(name="T2_id", referencedColumn="T2_id")
I don't know about EclipseLink, but Hibernate won't even let me specify a non-existant column name as the referencedColumn. If you insist, it should be
#JoinColumn(name="T2_id", referencedColumn="id")
so that it references a column that exists.
I am running into the exception below whenever I use an entity that I have defined.
org.hibernate.exception.SQLGrammarException: Invalid column name 'coordinator_sycs_coord_id'.
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:122)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
I will post below the entities involved and the query that Hibernate is generating. The context is two entities that have a many-to-many relationship in an association table. I find interesting that the query that Hibernate is generating is changing the column name even when I have it right in my annotations. See below:
#Entity
#Table(name = "sycs_coord")
public class SycsCoordinator {
#Id
#GeneratedValue
#Column(name = "sycs_coord_id")
Long id;
#OneToMany(mappedBy = "club", fetch = FetchType.LAZY)
Set<SycsCoordinatorClub> clubs;
//Standard setters and getters below
}
#Entity
#Table(name = "sycs_coord_clb")
#IdClass(SycsCoordinatorClubPk.class)
public class SycsCoordinatorClub {
#Id
#Column(name = "sycs_coord_id")
Long sycs_coord_id;
#Id
#Column(name = "clb_id")
String clb_id;
#ManyToOne
#PrimaryKeyJoinColumn(name = "sycs_coord_id", referencedColumnName="sycs_coord_id")
SycsCoordinator coordinator;
#ManyToOne
#PrimaryKeyJoinColumn(name = "clb_id", referencedColumnName = "Clb_id")
Club club;
}
I am not including the classes Club and SycsCoordinatorClubPk for now because they seem irrelevant to the problem. The query that Hibernate is generating some times is:
select
clubs0_.club_Clb_Id as club4_0_3_,
clubs0_.clb_id_fk as clb1_3_,
clubs0_.sycs_coord_id as sycs2_3_,
clubs0_.clb_id_fk as clb1_2_2_,
clubs0_.sycs_coord_id as sycs2_2_2_,
clubs0_.club_Clb_Id as club4_2_2_,
clubs0_.coordinator_sycs_coord_id as coordina5_2_2_,
clubs0_.lst_updt_dt as lst3_2_2_,
clubs0_.sycs_coord_secur_grp_cd as sycs6_2_2_,
sycscoordi1_.sycs_coord_id as sycs1_0_0_,
sycscoordi2_.sycs_coord_secur_level_id as sycs4_3_1_
from
sycs_coord_clb clubs0_
left outer join
sycs_coord sycscoordi1_
on clubs0_.coordinator_sycs_coord_id=sycscoordi1_.sycs_coord_id
where
clubs0_.club_Clb_Id=?
Notice that sometimes the column name coordinator_sycs_coord_id appears in the query, even when there is no such name in any of the annotations. Why is this?
You are mis-using the #PrimaryKeyJoinColumn annotation, hence the strange results:
It is used to join the primary table of an entity subclass in the
JOINED mapping strategy to the primary table of its superclass; it is
used within a SecondaryTable annotation to join a secondary table to a
primary table; and it may be used in a OneToOne mapping in which the
primary key of the referencing entity is used as a foreign key to the
referenced entity.
http://docs.oracle.com/javaee/5/api/javax/persistence/PrimaryKeyJoinColumn.html
You should probably be using the #JoinColumn instead:
#JoinColumn(name = "sycs_coord_id", referencedColumnName = "sycs_coord_id")
I am having following problem. I have a user entity that has a many to many relationship with other user entities. Hence I want to make a self-join with manytomany annotation. This relation is based on already existing table that is used across the system so I cannot make changes to the DB at all. So we have 2 tables User(Id, ShortName) and UserLink(ParentId, ChildId).
The annotation of ID is assigned to ShortName, but the actual keys in both User and UserLink are ID from User and ParentId and ChildId from UserLink.
I am trying to handle this the following way from the User entity:
#Id
#Column(name = "ShortName")
private String shortName;
#Column(name = "Id")
private long id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "UserLink",
joinColumns = { #JoinColumn(name = "ParentId", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "ChildId", referencedColumnName = "Id") })
private Collection<UserEntity> children;
Since the key in the User entity is on the ShortName field, I have to specify the "Id" as referenced column name param. If I don't do that, it takes the ShortName as the key and doesn't fetch any data.
When I try to do this the way I showed above, I get the following exception:
Caused by: org.hibernate.MappingException: Duplicate property mapping of **_entity_UserEntity_children found in **.entity.UserEntity
at org.hibernate.mapping.PersistentClass.checkPropertyDuplication(PersistentClass.java:486)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:476)
at org.hibernate.mapping.RootClass.validate(RootClass.java:268)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1287)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1729)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:84)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:904)
... 81 more
Do you have any idea how this could be fixed? One idea is that I could change the #Id in the entity and move it to the Id property that is used for joins, but this would need a lot of effort to rewrite bad existing code.
Anyway, is it possible to make a self-join manytomany on columns that are not keys?
Cheers
Adam
I am building a sample for ManyToMany relationship between: User(1) - ()AccessLevel() - (1)Role
I have implemented 3 classes in Java with hibernate implementation as follow:
Class User
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue
#Column(name="USER_ID")
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "access_level", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id", nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
Class Role
#Entity
#Table(name="role")
public class Role {
#Id
#GeneratedValue
#Column(name="role_id")
private Integer id;
#Column(name="role_name")
private String roleName;
Class AccessLevel
#Entity
#Table(name="access_level")
public class AccessLevel {
#Id
#GeneratedValue
private Integer id;
#Column(name="role_id")
private Integer roleId;
#Column(name="user_id")
private Integer userId;
Problem:
When I am persisting the User bean using merge method then an exception arise:
#Service
public class UserServiceImpl implements UserService {
#PersistenceContext
private EntityManager em;
#Override
#Transactional
public void save(User user) {
em.merge(user);
}
Exception
org.springframework.web.util.NestedServletException: Request process
ing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into access_level (user_id, role_id) values (?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
As you can see hibernate is trying to run this query:
insert into access_level (user_id, role_id) values (?, ?)
From my point of view it seems like hibernate is not generating the primary key for AccessLevel even though I have added the #GeneratedValue to the id attribute.
Note:
I am working on production environment with Postgresql and evelopment environment with HSQL database that creates all schemas from the begining based on the entities description. Both environments generate same issue.
Regards,
Cristian Colorado
Reason:
It seems for ManyToMany relationships you do not need to define a class for the "Joining Table". Therefore if I eliminate AccessLevel entity the logic would work perfectly fine. I explain further:
Explanation:
When I defined the User class I also described the relationship with Role:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "access_level", joinColumns = {
#JoinColumn(name = "user_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "role_id", nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<Role>(0);
Important thing here is I have told hibernate that User entity will relate to Role entity through a table known as "access_level" and such table will have user_id and role_id columns in order to join previous entities.
So far this is all hibernate needs in order to work the many to many relationship, therefore when mergin it uses that information to create and tun this script:
insert into access_level (user_id, role_id) values (?, ?)
Now, the problem cames when I defined a new entity for AccessLevel:
#Entity
#Table(name="access_level")
public class AccessLevel {
#Id
#GeneratedValue
private Integer id;
#Column(name="role_id")
private Integer roleId;
#Column(name="user_id")
private Integer userId;
Now I am telling hibernate that there is a table "access_level" related to AccessLevel entity and it has 3 columns, the most important would be Id which is primary key.
So I defined "access_level" twice!
Solution:
I eliminated the Entity for access_level table.
I re-write my production script in order to have "access_level" with
user_id/role_id columns only.
Note: It would be good to know how to add a primary key to the joining table without generating issues. An alternative would be adding a composed primary key in database(user_id/role_id) which would be independient from hibernate.
Why do you need a PK column in the join table? There will be a composite PK composed of user_id and role_id. Now, as you have discovered a JoinTable for #ManyToMany will only ever have two columns and at some point you may require additional data about this relationship.
e.g.
user_id
role_id
date_granted
You may then want to use your AccessLevel entity however you replace the #ManyToMany with #OneToMany from User to AccessLevel and optionally from Role > AccessLevel.
The Hibernate docs themselves advise against #ManyToMany:
Do not use exotic association mappings:
Practical test cases for real many-to-many associations are rare. Most
of the time you need additional information stored in the "link
table". In this case, it is much better to use two one-to-many
associations to an intermediate link class. In fact, most associations
are one-to-many and many-to-one. For this reason, you should proceed
cautiously when using any other association style.