Creating new JPA Entity with EmbeddedID - jpa

I have a very simple set of tables modelling a many to many relationship.
Foo -< FooBar >- Bar
Which is working fine when I select data but when I try to create a new instance of the relationship I can getting errors because JPA is trying to insert nulls.
I am having to set both the #ManyToOne values as well as the key (which represent the same things) and this makes me think this is not setup correctly.
Question is therefore how do I correctly setup the annotations to create a new FooBar relationship?
Entities
#Entity
#Table(name = "FOO")
public class Foo implements Serializable {
#Id
#Column(name = "FOO_ID")
private int id;
#column(name = "FOO_DESC")
private String desc;
#OneToMany(mappedBy = "foo")
private List<FooBar> fooBars;
//getters / setters / hashCode / equals
}
#Entity
#Table(name = "BAR")
public class Bar implements Serializable {
#Id
#Column(name = "BAR_ID")
private int id;
#column(name = "BAR_DESC")
private String desc;
#OneToMany(mappedBy = "bar")
private List<FooBar> fooBars;
//getters / setters / hashCode / equals
}
Intersection Table (and Key)
#Embeddable
public class FooBarKey implements Serializable {
private int fooId;
private int BarId;
//getters / setters / hashCode / equals
}
#Entity
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#EmbeddedId
private FooBarKey key;
#MapsId("fooId")
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#MapsId("barId")
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
Creating Relationship
Finally I am creating a new intersection instance:
//foo and bar exist and are populated
FooBar fb = new FooBar();
FooBarKey fbk = new FooBarKey();
fbk.setFooId(foo.getId());
fbk.setBarId(bar.getId());
fb.setKey(fbk);
fb.setDesc("Some Random Text");
entityManager.persist(fb);
at which point JPA errors with the insert saying it cannot insert null into FOO_ID.
I have checked and at the point I persist the object, the key is populated with both Foo and Bar IDs.
If I add
fb.setFoo(foo);
fb.setBar(bar);
prior to the persist it works but should the #MapsId not effectively tell JPA to map using the key?
I presume that I should be setting both the #ManyToOne and key values which are logically the same thing so I must have something not configured correctly?
I am using Eclipselink if that makes a difference.

When you use mapsId, you are telling JPA that this relationship, and the value of the target entity's primary key, is used to set the mapping named within the mapsId value. JPA then uses this relationship mapping to set the foreign key AND the basic mapping value when it flushes or commits. In your case, you left the relationship NULL, which forces the FK to be null when it gets inserted.
This allows sequencing to be delayed, as you may not have the primary key generated in the referenced entity when creating and traversing the graph - JPA will calculate and populate the values and propagate them through the graph when it needs them.
If you aren't using the ID class in your model, the simplest solution is just to remove it and avoid the overhead:
#Entity
#IdClass(package.FooBarKey.class)
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#Id
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
public class FooBarKey implements Serializable {
private int foo;
private int bar;
}
IdClass has similar restrictions to what is needed for #EmbeddedId but with one more - the names within it must match the property names designated with #Id, but the types must be the same as the ID class within the referenced entity. Pretty easy if you are using basic mappings within Foo and Bar, but can be more complex.
Adding more to your composite key is easy:
#Entity
#IdClass(package.FooBarKey.class)
#Table(name ="FOO_BAR_XREF")
public class FooBar implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")
private Foo foo;
#Id
#ManyToOne
#JoinColumn(name = "BAR_ID", referencedColumnName = "BAR_ID")
private Bar bar;
#Id
private int someValue
#Column(name = "DESC")
private String desc;
//getters / setters / hashCode / equals
}
public class FooBarKey implements Serializable {
private int foo;
private int bar;
private int someValue
}
JPA will populate your foreign keys for you when the relationships are not-null, but any other fields require either sequencing or your own mechanisms to ensure they are populated prior to insert, and all Ids should be treated as immutable within JPA.

Related

How to name Foreign Key with #CollectionTable in JPA?

I need help with resolving our problem with naming FK in JPA. We have one embeddable entity e.g. Foo which is used as collection in another one entity Bar.
embeddable entity:
#Embeddable
public class Foo{
#Column(name = "foo1")
private String foo1;
#Column(name = "foo2")
private String foo2;
}
main entity:
#Entity
#Table(name = "Bar")
public class Bar {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
#Id
private Long id;
#ElementCollection
#CollectionTable(name = "foos", joinColumns = #JoinColumn(name = "bar_id", foreignKey = #ForeignKey(name = "foo_bar_fk"), nullable = false))
private List<Bar> bars;
}
When I generated tables in database (postgres) foreign key is called fdk44l345k64 instead foo_bar_fk. Can you tell me how to fix it? Thanks.

Spring Data JPA does findAll retrieve data from associated table in a onetoone mapping?

I am using Spring Data JPA. I have 2 tables as follows:
Investment and Investment_Type. There is a one to one relationship between Investment and Investment_Type.
My Investment class is as follows:
#Entity
public class Investment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int investmentId;
#NotNull(message = "Cannot be empty")
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "investment_type_id")
private InvestmentType investmentType;
#NotNull(message = "Cannot be empty")
private String investmentNumber;
//getter and setter methods
}
My InvestmentType class is as follows:
#Entity
public class InvestmentType {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Integer investmentTypeId;
private String investmentTypeName;
//getter and setter methods
}
My InvestmentRepository is as follows:
public interface InvestmentRepository extends JpaRepository <Investment, Integer>{
}
My controller has the following code:
List<Investment> investments = investmentRepo.findAll();
for(Investment investment:investments){
logger.info(" Got investment with id "+investment.getInvestmentId());
if(investment.getInvestmentType() != null){
logger.info("Investment Type is "+investment.getInvestmentType().getInvestmentTypeName());
}
else{
logger.info("null investment type ");
}
}
However, the for loop always outputs "null investment type"
So it appears that the findAll method does not retrieve the data from the InvestmentType table to which there is a OneToOne mapping.
Am I doing something wrong? Do I need to do something explicit in order to also retrieve data from the associated table with the mapping?
one-to-one association is the only one that can not be proxied.Add fetch type lazy and use mappedby on related table colum.
Parent table
#NotNull(message = "Cannot be empty")
#OneToOne(fetch = FetchType.LAZY, optional = false)
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "investment_type_id")
private InvestmentType investmentType;
Related table
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#OneToOne(mappedBy = "investment")
private Integer investmentTypeId;

using #Embedabble with a foreign key and manyToMany relation

I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.

Child entity with Secondary Table and Compound Key spread over two tables

After much debate on choosing an approach for an internationalized database design I went with having two tables for each table that requires translation. I'm having some trouble with ORM in the following case.
So I have the following tables:
cat cat_t subcat subcat_t
------ ------- ---------- ------------
id (pk) cat_id(pk,fk) id(pk) subcat_id(pk,fk)
locale(pk) cat_id(fk) locale(pk)
name name
#Entity
#Table(name = "cat")
#SecondaryTable(name = "cat_t",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "cat_id",
referencedColumnName = "id"))
#IdClass(TranslationKey.class)
public class Category {
#Id
private long id;
#Id
#Column(table = "cat_t")
private String locale;
#Column(table = "cat_t")
private String name;
#OneToMany(fetch = FetchType.LAZY)
private List<SubCategory> subCategories;
// getters and setters
}
#Entity
#Table(name = "subcat")
#SecondaryTable(name = "subcat_t",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "subcat_id",
referencedColumnName = "id"))
#IdClass(TranslationKey.class)
public class SubCategory{
#Id
private long id;
#Id
#Column(table = "subcat_t")
private String locale;
#Column(table = "subcat_t")
private String name;
#Column(name = "cat_id")
private long categoryId;
// getters and setters
}
public class TranslationKey implements Serializable {
private long id;
private String locale;
// getters and setters
}
My goal is for subcategories to only pull back the subcategories for the locale of the parent. I think I have some options including, querying the subcategories separately and making the field transient or pull everything back (all subategories for all languages) and then just filter out the ones I want.
The issue I have had with #JoinColumn is that locale is part of the secondary table for both cat can subcat and so when I try the referencedColumn that may not be allowed since its not in the same table?. I'm using EclipseLink but I'm not really tied to a JPA Provider.
Any help/guidance is much appreciated
cat seems to have a one to many relationship with cat_t, since there can be many rows in cat_t for a single cat entry. This is not ideal to have in a single Entity, since it means you will have instances sharing data, and endless headaches if you make changes.
A better approach that makes updates possible is to map the cat and cat_t to separate Entities, where Cat has a collection of Cat_t?Local instances. The entity mapping to cat_t can use the local and its ManyToOne back pointer to Cat as its Id:
#Entity
#IdClass(TranslationKey.class)
public class Local {
#ID
private String locale;
#ID
#ManyToOne
private Cat cat;
}
public class TranslationKey{
string local;
long cat;
}

Mapping JPA entity relationships

I would like some advice on how to best layout my JPA entity classes. Suppose I have 2 tables I would like to model as entities, user and role.
Create Table users(user_id primary key,
role_id integer not null )
Create table role(role_id primary key,
description text,
)
I create the following two JPA Entities:
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private Long roleId;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#Column(name = "role_id")
public Long getRoleId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
}
Role Entity:
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public Long getDescrition(){}
#ManyToOne()
#JoinColumn(name="role_id")
public Role getRole() {}
}
Would the correct way to model this relationship be as above, or would I drop the private Long roleId; in Users? Any advice welcomed.
When I map it this way, I receive the following error:
org.hibernate.MappingException: Repeated column in mapping for entity:
Yes, you would drop the private Long roleId mapping when you have a #ManyToOne on the same column.
As the error implies, you can only map each column in an #Entity once. Since role_id is the #JoinColumn for the #ManyToOne reference, you cannot also map it as a property.
You can, however, add a convenience method to return the role ID, like
public Long getRoleId() {
return role.getId();
}