A Profile table has a one to many association with a Privilege table. The privilege table has a multipart key, of a profile_id and a privilege_id. I want to join from the Profile table to the Privilege table only on the profile_id and get back a collection of privileges.
In my Profile class I have
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "PROF_ID")
public List<ProfilePrivilegeEO> getProfilePrivileges()
{
return m_profilePrivileges;
}
My Privilege class has
private ProfilePrivilegeId m_profileId;
#EmbeddedId
public ProfilePrivilegeId getProfileId()
{
return m_profileId;
}
Where ProfilePrivilegeId is
#Embeddable
public class ProfilePrivilegeId
implements Serializable
{
private Integer m_profileId;
private Integer m_privNumber;
#Column(name = "PROF_ID")
public Integer getProfileId()
{
return m_profileId;
}
#Column(name = "PRIV_NUM")
public Integer getPrivNumber()
{
return m_privNumber;
}
.....
}
However, when i do that the static weaver says:
The #JoinColumns on the annotated element [method getProfilePrivileges] from the entity
class [class com.acme.ProfileEO] 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.
However, the Profile table has no knowledge of privilege_ids... I don't see why JPA should demand that i specify both keys of the privilege table, that's just an arbitrary decision made by jpa with no valid reason why... What do i need to do to get this to work? (I am using EclipseLInk.)
Create an PrivilegeId class that encapsulate the ids. Make that class #Embedded and put it in the Privilege with #EmbeddedId.
In the PrivilegeId class, put an #OneToMany to the Profile and the privilege id.
Related
I have the following table in the DB:
material (id_mat, name, initial_weight, cargo_number, exp_date, left_amount)
I had to add an additional table, which shows constructions that were built using the materials from material table. Here how it looks:
material_construction (mat_id, construction_number)
I then created an entity class called MatConstructionMapping for the table material_construction:
#Entity(name = "material_construction")
public class MatConstructionMapping implements Serializable {
private static final long serialVersionUID = 1739614249257235075L;
#Id
#OneToOne
#JoinColumn(name = "mat_id", referencedColumnName = "id_mat", insertable=false, updatable=false)
private Material mat;
#Column(name="construction_number")
private Integer number;
public Integer getConNumber() {
return number;
}
}
And, added following getter in the Materialentity:
#OneToOne
#JoinColumn(name = "mat_id")
public MatConstructionMapping getMaterialConstructionNumber() {
return conNumber;
}
The issue is that, when I am retrieving the conNumber for any materials, its always null, however there are the values in the DB. What am I doing wrong?
you cannot have JoinColumn at both sides, #JoinColumn should be at the owning entity which you can define in any side in one to one relation, the other side should have mappedBy attribute to indicate the reverse relation, say for example MatConstructionMapping is the owning entity, then you should edit your Material
#OneToOne(mappedBy="mat")
public MatConstructionMapping getMaterialConstructionNumber() {
return conNumber;
}
I'm attempting to implement a limited type of object level ACL and its lead me to a place where I'm attempting to create a #OneToOne relationship using a composite key with a constant and dynamic value.
I have an Entity with a database id and a constant value defined in the class.
public class Entity{
private static final int objectType = 1;
#Id
Integer id;
}
I have an access_levels table with a composite key of objectId and objectType.
public class AccessLevel {
#EmbeddedId
private AccessLevelKey accessLevelKey;
#Embeddable
class AccessLevelKey implements Serializable{
private Integer objectType;
private Integer objectId;
....
}
}
Schema of access_levels
CREATE TABLE access_levels(
object_type INTEGER NOT NULL,
object_id INTEGER NOT NULL,
....
CONSTRAINT access_levels_type_id PRIMARY KEY (object_type, object_id)
);
I'm attempting to come up with a one to one relationship that Entity can use to fetch and update its associated AccessLevel
After taking a look a the docs on Non-Standard Joins it seems like I need something like this,
Inside of Entity:
#OneToOne
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "object_id"),
#JoinColumn(name = "access_levels.object_type", referencedColumnName = "1"),
})
private AccessLevel accessLevel;
However this throws a hibernate MappingException at app launch
Caused by: org.hibernate.MappingException: Unable to find column with logical name: 1 in access_levels
Thanks!
my problem is that I cannot save my entity because it contains another entity, mapped by a key that is also a part of this table's primary key. The table looks like this:
table C:
+-----+------+
| id_A | id_B |
+-----+------+
..where idA is the primary key of table A with EntityA and idB the primary key of table B with EntityB.
so its basically a n-to-m relation. This is the entity I'm using for table C:
#Entity
public class EntityC {
private long idA;
private EntityB b;
#Id
#Column(name = "id_A")
public long getIdA() {
return idA;
}
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_B")
public EntityB getB() {
return b;
}
...setters are here...
}
Please note that id_A is mapped as is (the id), while id_B is mapped as its object representation, EntityB. This is what I want to do with it:
EntityC c = new EntityC();
c.setIdA(123);
c.setB(new EntityB());
em.persist(c);
tx.commit();
em.close();
I want to persist EntityB ONLY IF I can persist EntityC.
on tx.commit() I get this exception: org.hibernate.TransientObjectException: object references an unsaved transient instance
I suppose this happens because part of the primary key, id_B, is not saved. But i set cascading to all so there should be no problem!
Why is this not working?
EDIT:
When I do this:
em.persist(c.getB());
em.persist(c);
it works. But can't Hibernate/JPA do that automatically? I thought that's what cascading is good for.
EDIT2:
added an embeddedId instead of id_A and id_B:
#Embeddable
public class EntityCID implements Serializable {
public long idA;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_B", referencedColumnName = "id")
public EntryB b;
}
EntityC now looks like:
#Entity
public class EntityC implements Serializable {
private EntityCID id;
...
#EmbeddedId
public void getId() {
return id;
}
}
but I still get the transient object exception if I don't em.persist(c.getId().b); before em.persist(c). Sticking to that, although it is ugly.
#Trein: it is not bidirectional. EntityB code:
#Entity
public class EntityB implements Serializable {
public long id;
public String text;
}
If you think about it what you are seeing makes perfect sense.
EntityC is is the 'owning side' of the relationship C<>B: it defines the JoinColumn and EntityB has the 'mappedBy' attribute.
So on saving C, order of events would normally be:
insert into C/update C
insert into B/update B
Now in your case this causes issues as obviously C can only be saved if B has been persisted first.
In terms of your statement above: I want to persist "EntityB ONLY IF I can persist EntityC." How can this ever be the case?
JPA has a concept of 'Derived Identifiers', which I am not overly familiar with however is defined in the book Pro JPA as occurring when:
When an identifier in one entity includes a foreign key to another
entity, we call it a derived identifier. Because the entity containing
the derived identifier depends upon another entity for its identity,
we call the first the dependent entity. The entity that it depends
upon is the target of a many-to-one or one-toone relationship from the
dependent entity, and is called the parent entity
Now, despite the original advice that you had two #Id attributes defined and this was wrong it would however appear that having an additional #Id on a 1-2-m is in fact valid in JPA 2 for precisely this case.
The book gives a number of ways of dealing with Derived Identifiers however one example given below looks fairly similar to your case. So you may want to investigate further the #MapsId attribute.
#Entity
public class Project {
#EmbeddedId private ProjectId id;
#MapsId("dept")
#ManyToOne
#JoinColumns({
#JoinColumn(name="DEPT_NUM", referencedColumnName="NUM"),
#JoinColumn(name="DEPT_CTRY", referencedColumnName="CTRY")})
private Department department;
// ...
}
#Embeddable
public class ProjectId implements Serializable {
#Column(name="P_NAME")
private String name;
#Embedded
private DeptId dept;
// ...
}
See further:
How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6
Is it a bidirectional relationship? I would suggest you to remove #Id getB() and perform the modifications:
#OneToOne(cascade = CascadeType.ALL, mappedBy = "id_B")
#PrimaryKeyJoinColumn(name = "id_B")
public EntityB getB() {
return b;
}
Your entity class must have only one attribute annotated with #Id. Usually when you need this, you create a class that will store both properties and this will act as a Id Class.
You can not pass new Entity() for reference. Because it won't have any values in it(even primary key). So how can hibernate will insert it as foreign key to the table. And cascade will save your parent object if its not saved,no need to call save method for all. But when you passing new object it won't do.
My entity has two foreign keys, one of those is the primary key. I read in the JPA Wiki that if the child's (Procurement) primary key is the same as the parent's (Article) one, you establish a #OneToOne relationship.
#SuppressWarnings("serial")
#Entity
public class Procurement implements Serializable {
#Id
#OneToOne
// the child's primary key is the same as the parent
#JoinColumn(name = "articleId", referencedColumnName = "id")
private Article article;
#OneToOne
#JoinColumn(name = "supplierId", referencedColumnName = "id")
private Supplier supplier;
Following the standard approach, the JpaRepository should look like this:
#Repository
public interface IProcurementRepository extends
JpaRepository<Procurement, Article>
However, if I want to call the findOne-method (which looks for primary key) delivering an "Article" object, Hibernate throws an exception.
org.hibernate.TypeMismatchException: Provided id of the wrong type for
class de.willms.spring.myerp.model.Procurement. Expected: class
de.willms.spring.myerp.model.Procurement, got class
de.willms.spring.myerp.model.Article
The DB table structure:
Table "article" (ID, shortText)
Table "supplier" (ID, name)
Table "procurement" (articleID, supplierID, price)
What do I have to change so that I can find a "Procurement" record by the corresponding "Article" object?
Finally, I found a special function in Hibernate (amazing, but hard to understand in the documentation) leading to a solution.
As soon as the primary key consists of several columns or, like in my case, relates to other tables, a special ID class has to be written.
#Embeddable
public class Procurement_ID implements Serializable {
/**
* This attribute establishes the 1:1 connection to a record in the
* "article" table. The column "articleId" is a foreign key to the column
* "id" in the {#link Article} entity. (Warning: This is Hibernate
* specific!)
*/
#OneToOne
#JoinColumn(name = "articleId", referencedColumnName = "id")
private Article article;
/**
* This attribute establishes the 1:1 connection to a record in the table
* "supplier". The column "supplierId" is a foreign key to the column "id"
* in the {#link Supplier} entity. (Warning: This is Hibernate specific!)
*/
#OneToOne
#JoinColumn(name = "supplierId", referencedColumnName = "id")
private Supplier supplier;
(Due to a more "normalised" data model, I switched to the composite primary key.)
Because of the #Embeddable annotation, this ID can be injected into the entity class.
#Entity
public class Procurement implements Serializable {
/**
* The composite primary key of the underlying table is defined in the
* {#link Procurement_ID} class.
*/
#EmbeddedId
private Procurement_ID procid;
With me using this approach, Hibernate inserts a new record with positive foreign key check:
[DEBUG] Generated identifier: component[article,supplier]{article=de.willms.spring.myerp.model.Article#1, supplier=de.willms.spring.myerp.model.Supplier#1}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
... (Flusing) ...
[DEBUG] Listing entities: ...
de.willms.spring.myerp.model.Procurement{price=2.5, deliveryTime=10,
procid=component[article,supplier]{article=de.willms.spring.myerp.model.Article#1,
supplier=de.willms.spring.myerp.model.Supplier#1}, priceUnit=$}
However, it is a pity that the JpaRepository cannot inject find()-method stubs concering only one part of the primary key. It is "unable to resolve the attribute against the path". You are welcome to comment!
You still have to use an id of type int or long. The difference is that instead of joining with #JoinColumn, you have to use #PrimaryKeyJoinColumn. You class should look like this.
#SuppressWarnings("serial")
#Entity
public class Procurement implements Serializable {
#Id
#GeneratedValue(generator = "articleForeignGenerator")
/* Hibernate-specific generator needed for shared primary key */
#GenericGenerator(name = "articleForeignGenerator", strategy = "foreign", parameters = #Parameter(name = "property", value = "article"))
#Column(name = "articleId")
private int articleId;
#OneToOne
// the child's primary key is the same as the parent
#PrimaryKeyJoinColumn
private Article article;
#OneToOne
#JoinColumn(name = "supplierId", referencedColumnName = "id")
private Supplier supplier;
Note that I use a hibernate-specific generator for the ID. It may be different for your JPA provider.
I am using Hibernate and JPA. If I have two simple entities:
#Entity
#Table(name = "container")
public class Container {
#Id
#Column(name="guid")
private String guid;
}
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name="guid")
private String guid;
#Column(name="container_guid")
private String containerGuid;
}
and I want to insure that inserting an Item fails if the referenced Container does not exist. I would prefer not to have a Container object populated inside the item object (ManyToOne), how would I do this if it is possible to do?
You can declare arbitrary constraint using columnDefinition attribute:
#Column(name="container_guid",
columnDefinition = "VARCHAR(255) REFERENCES container(guid)")
private String containerGuid;
Note, however, that Hibernate doesn't know anything about this constraint, so that, for example, it may not perform inserts in proper order with respect of it and so on.
Therefore it would be better to create a #ManyToOne relationship. If you are afraid of extra SQL query for Container needed to set this property, you can use Session.load()/EntityManager.getReference() to get a proxy without issuing actulal query.
Try using below relationship mapping
RelationShip Mapping
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#ManyToOne()
#ManyToMany()
<>
#JoinColumn(name="<>")