Moving #OneToMany from Entity to Embeddable - jpa

It's my first post, so I hope I do it the right way. I have searched two days for an equivalent Problem, but did not find anything.
Here is what I did:
We have an Entity, that contains (beside others) the folowing fields:
#Entity
#Access(AccessType.FIELD)
#Table(name = "component")
public class Component {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
#OneToMany
#JoinTable(name = "component_dokumentation",
joinColumns = #JoinColumn( name = "component_id" ),
inverseJoinColumns = #JoinColumn(name = "dokumentation_id"))
private Set<FileType> dokumentation;
private Long keySisMf = 0L;
.
.
// Getter and Setter and stuff
}
After one year of usage we have found out, that our Entity became too big and that we have to use DTO Objects to transfer data to the Client, modify them and return them to the Server. For this purpose we modelled an embeddable Entity ComponentAttributes.
So right now it Looks like:
#Entity
#Access(AccessType.FIELD)
#Table(name = "component")
public class Component {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
.
.
#Embedded
private ComponentAttributes componentAttributes;
.
.
}
#Embeddable
#Access(AccessType.FIELD)
public class ComponentAttributes {
private static final long serialVersionUID = 1L;
#OneToMany
#JoinTable(name = "component_dokumentation",
joinColumns = #JoinColumn( name = "component_id" ),
inverseJoinColumns = #JoinColumn(name = "dokumentation_id"))
private Set<FileType> dokumentation;
private Long keySisMf = 0L;
.
.
// Getter and Setter and stuff
}
We did not change anything in the Database. We have encountered Problems in setting values for the set documentation. The field keySisMf is not a Problem. The Problems are just related to the documentation (I must add that FileType is just a Basic Entity consisting of an id and several Strings, so nothing Special). Getting the values and transfering them to the Client is fast and correct. Telling the Server to Change keySisMf is not a Problem. Telling the Server to add or remove a FileType instance simply does not work. No Errors but no changes.
We have logged the JPA generated SQL and there is no SQL generated for component.getComponentAttributes().setDokumentation(fileSet).
We use a Glassfish 4.1.1 Server with an ORACLE Database. Did I miss something when moving dokumentation from Component to ComponentAttributes????
Thanks for your help and patience.
Chris

Related

Spring JPA Postgresql - Composite keys are duplicated when one is not

I am having some troubles and hope you can help me, I have the following entity:
App class:
#Entity
#Table(name = "apps")
public class App {
#Id
#Column(length = 15)
private String name;
#Column(length = 40)
private String web;
#Column(length = 50)
private String mailDomain;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "app")
private List<SocialNetwork> socialNetworks = new ArrayList<>();
//getters, setters, equals and hash
Social Network Class:
#Entity
#Table(name = "social_networks")
#IdClass(SocialNetworkCompositeKey.class)
public class SocialNetwork {
#Id
#Column(length = 15)
private String name;
#Id
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "app")
private App app;
#Column(nullable = false, length = 40)
private String url;
//getters, setters, equals and hash
SocialNetworkCompositeKey Class:
public class SocialNetworkCompositeKey implements Serializable {
private String name;
private String app;
//equals and hash
Now whenever I try to insert an App either in my Program or directly in the DB, I get the Exception:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "uk_590itpuvpqppd9f0g2w5y8bml"
Detail: Key (app)=(Uli App) already exists.
This while trying to insert 2 records with:
app name url
----+---------+-----------+---------
1 | Uli App | Twitter | http...
----+---------+-----------+---------
2 | Uli App | Linkedin | http...
----+---------+-----------+---------
I am using the latest version of both Spring boot and JPA. So I use JpaRepository for my repositories. Even if I try to enter those rows manually in the DB with pgAdmin it'll give me the same error.
I am not sure if it's related but I use ddl-auto: update from hibernate to auto create the tables.
I hope you guys can help me, cheers.
as #Morteza said my relationships were wrong. With this and other tutorial I found after digging alot (I really Googled alot before posting this) I was able to fix it by changing the relationship from #OneToMany to #ManyToOne in the Social Networks class. These are changes I've made:
App class:
#OneToMany(mappedBy = "app", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<SocialNetwork> socialNetworks = new ArrayList<>();
Social Network class:
#Id
#ManyToOne(fetch = FetchType.LAZY)
private App app;
Thanks for the help guys!

Why my foreign key is null when saving to h2 db, using JPA

What is the difference between these 2 codes. The 1st one shows null on my foreign key which is individualId. The 2nd one is not. Why?
//1st code:
#Entity
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Individual {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "INDIVIDUAL_ID")
private Long individualId;
#OneToMany(mappedBy="individual",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Identification> identifications = new ArrayList<Identification>();
}
#Entity
public class Identification {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "IDT_ID")
private Long id;
#ManyToOne
#JoinColumn(name="individualId")
private Individual individual;
//second code
//replaced #OneToMany in the first code & then i just dont add #ManyToOne in the Identification Class and it works fine. Why?
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "INDIVIDUAL_ID", referencedColumnName = "INDIVIDUAL_ID")
private List<Identification> identifications = new ArrayList<Identification>();
When i search for JPA tutorial in google the 1st code is the one that i always read. declare #OneToMany in the parent class and add mappedBy, declare #ManyToOne in the child class. But why the 2nd code works perfect than the 1st code? it just let me declare #OneToMany only in the parent class ?
In the class Identification the name of the #JoinColumn does not match any column in your class Individual. It must be the name of the column in the database, which is INDIVIDUAL_ID:
#JoinColumn(name="INDIVIDUAL_ID")

How to show 2 different type of mappings between 2 entity classes, holding each other's reference

The mappings between the 2 tables(Department and Employee) is as follows (Link for the image showing mapping is also provided):
Every department has one and only one department head.
Every department can have more than one employee.
dept_id and empId are primary keys of their respective tables.
dept_head(It is the Employee Id) and dept are foreign keys of their
respective tables.
Mapping Employee and Department table
I created entity classes for the above 2 tables (The structure is provided below).
Employee Class:
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "empId")
private Integer empId;
#Size(max = 45)
#Column(name = "name")
private String name;
#Size(max = 45)
#Column(name = "address")
private String address;
#Size(max = 45)
#Column(name = "grade")
private String grade;
#Size(max = 45)
#Column(name = "email")
private String email;
#JoinColumn(name = "dept", referencedColumnName = "dept_id")
#ManyToOne
private Department deptartment;
.. ...
}
Department class:
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 8)
#Column(name = "dept_id")
private String deptId;
#Size(max = 45)
#Column(name = "name")
private String name;
#JoinColumn(name = "dept_head", referencedColumnName = "empId")
#OneToOne
private Employee deptHead;
#OneToMany(mappedBy = "deptartment")
private List<Employee> employeeList;
....
...
}
If I am adding mappedBy in Employee Class (like I did in Department), to show OneToOne mapping between empId and deptHead,the code is compiling and running. However, If I do not add the mappedBy statement in Employee class, as the above code shows, the code still compiles and runs fine.
I would want to know why the code above works even if I am not providing mappedBy in employee class.
If anybody can help me clearing the above doubts and explaining the logic behind its working would be great as I am new to this.
It is not quite clear where you tried to user it with and without the mappedBy attribute.
But if I get your question correctly, you ask why you can have only one or both sides annotated?
It depends on which side is the source and destination of your relation or wheter it's bi-directional. On the Java-side you can have a relation always in both directions due to object references, but on the Database-side, you might only have it in one direction.
Check out JPA Wiki book on that topic for more details.
Additionally, the API doc for OneToOne states:
Specifies a single-valued association to another entity that has
one-to-one multiplicity. It is not normally necessary to specify the
associated target entity explicitly since it can usually be inferred
from the type of the object being referenced. If the relationship is
bidirectional, the non-owning side must use the mappedBy element of
the OneToOne annotation to specify the relationship field or property
of the owning side.

The attribute [] is not present in the managed type

I'm doing an application that has this relation ship: A personal contact has an Email.
So i'm trying to find the Emails from the personal contact and I'm doing this query using Criteria but always return IllegalArgumentException:
#Override
public Email findByEmail(PersonalContact personalContact) {
CriteriaBuilder criteriaBuilder = entityManager().getCriteriaBuilder();
CriteriaQuery<Email> criteriaQuery = criteriaBuilder.createQuery(Email.class);
Root<Email> email = criteriaQuery.from(Email.class);
criteriaQuery.where(criteriaBuilder.equal(
email.get("personalContact"), criteriaBuilder.parameter(PersonalContact.class, "personalContact")));
TypedQuery<Email> typedQuery = entityManager().createQuery(criteriaQuery);
typedQuery.setParameter("personalContact", personalContact);
return typedQuery.getSingleResult();
}
Personal contact is like a foreign key.
And here is my Email class:
#Entity
#Table(name = "Email")
public class Email implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String mainEmail;
private List<String> secondaryMail;
#JoinColumn(name = "personal")
#OneToOne(fetch = FetchType.LAZY)
private PersonalContact pContact;
and here is my Personal Contact class:
#Entity
#Table(name = "PERSONALCONTACT")
public class PersonalContact extends Contact implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "PERSONAL_ID")
private Long id;
//Other variables
#OneToOne(fetch=FetchType.LAZY, mappedBy="personal")
private Email email;
And every time I execute the query this is the return:
Exception in thread "AWT-EventQueue-0"
java.lang.IllegalArgumentException: The attribute [personalContact] is
not present in the managed type [EntityTypeImpl#1230307250:Email [
javaType: class csheets.ext.crm.contact.Email descriptor:
RelationalDescriptor(csheets.ext.crm.contact.Email -->
[DatabaseTable(Email)]), mappings: 5]].
I did some search and the others programmers said the problem was on the name of the variables... but i guess the names of the variables are correct.
So what I'm doing wrong? perhaps the relationship between that two classes?
Thank you!
If you read the exception message carefully, you'll find that it is complaining that class Email does not have a property (attribute) called personalContact, and indeed, there is no such property. Presumably you meant the pContact property?
(Mistakes such as this are why I recommend querying JPA via Querydsl: code completion would likely have prevented this mistake, and even if not, you would have gotten a clear compiler message when trying to use a non-existing property)

JPA EclipseLink Parent Child

I am currently using 2 classes that have a OneToMany relation. One class contains catalogs (you can think of it as book); an other class contains template (you can think of it as pages). In this scenario, one template can belong only to one catalog hence I used the OneToMany relation.
My application goes very well until I restart the service. It is currently running on Hana Cloud Platform under MaxDB. I am using JPA and eclipselink (I used #AdditionalCriteria to manage my multi-tenancy as the multi-tenancy offered by JPA does not allow me to make queries on multiple tenants).
Here is an extract of my code for the Catalog:
#Entity
#Table(name = "Catalog")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Catalog implements Serializable {
private static final long serialVersionUID = -3906948030586841482L;
#Id
#GeneratedValue
private long id;
[...]
#OneToMany(cascade = ALL, orphanRemoval = false, fetch = EAGER, mappedBy = "catalog")
private Set<Template> templates = new HashSet<Template>();
[...]
public void setTemplate(Template template) {
this.templates.add(template);
}
}
The code for Template is the following:
#Entity
#Table(name = "Template")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Template implements Serializable {
private static final long serialVersionUID = 5268250318899275624L;
#Id
#GeneratedValue
private long id;
[...]
#ManyToOne(cascade = ALL, fetch = EAGER)
#JoinColumn(name = "catalog_id", referencedColumnName = "id")
private Catalog catalog;
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
if(!catalog.getTemplate().contains(this))
catalog.getTemplate().add(this);
}
}
In my Servlet, I use only the Catalog to make operations. If I have to save a template, I read it from the catalog, make the modifications in the template and persist the catalog.
It works very well until I restart my service.
The catalog does not have any references to the templates anymore BUT the template still have a reference to the catalog it used to belong to.
Can you please point me into the right direction?
Thanks