Unidirectional OneToMany using Foreign Key Column - java

I want to maintain ACL information for each of my entities. For this i have the following entity:
#Entity
public class ACLEntry {
...
#Id
private Long id;
private Long sid;
private Integer permissionMask;
}
The entity for which the acl information should be maintained looks like the following:
#Entity
public class Folder {
...
#Id
private Long id;
#OneToMany
#JoinColumn(name="entity_id")
private List<ACLEntry> aclEntries;
}
From the hibernate docs:
A unidirectional one to many using a foreign key column in the owned entity is not that common
and not really recommended
Why is this not recommended and why should i use a jointable ?
Is there a better way to solve this ?

ACLEntry should have a reference to parent Folder #ManyToOne

Related

JPA OneToMany-Mapping without mapping class for Many-side

In a Spring Boot application using JPA 2.1 and Hibernate, there are two (PostgreSQL) tables of interest:
entity external_id
-- --
id serial id serial
... entity_id int
... external_id int
The relation between entity and external_ids is obviously OneToMany, which I want to use in the JPA mapping as well. A simple way to do this is to create #Entity-mappings for each table and use a #OneToMany-relation:
#Entity
public class Entity {
#Id
private Integer id;
#OneToMany(mappedBy= "entityId")
private Set<ExternalId> externalIds;
}
#Entity
public class ExternalId {
#Id
private Integer id;
#ManyToOne
private Integer entityId;
private Integer externalId;
}
But since the table external_ids just holds a list of numbers for each member of entity, I would like to go without an explicit mapping of the table external_id and immediately map the values of external_id.external_id:
#Entity
public class Entity {
#Id
private Integer id;
#OneToMany(???)
private Set<Integer> externalIds;
}
Is this possible with JPA 2.1 and if so how?
You can utilize #ElementCollection for this purpose:
#ElementCollection
#CollectionTable(name = "TableName", joinColumns=#JoinColumn(name = "JoinColumnName"))
private Set<Integer> externalIds;

JPA repository - improve findAll() performances

I am using JPA repository, I need to retrieve a whole Mysql table(40000 records) wich has 5 foreign keys towards smaller tables (500 records). I need one field of each of these 5 tables.
If I call a JPArepository findall(), it takes a few seconds to retrieve all the data.
I need it to be faster. Is there a way to do that?
I don't know what would be the best solution, if it can be done on mysql side, or must be done on Java side.
All the tables are well mapped to JPA entities :
#Entity
#Table(name = "T_CLIENT")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "code")
private String code;
#OneToOne
private Seller seller;
#OneToOne
private Language language;
#OneToOne
private Address address;
#OneToOne
private Country billCountry;
#OneToOne
private ClientType clientType;
}
Thank you for your answers.
You can choose loading fields from foreign keys tables using EntityGraph mechanism.

Why in this Hibernate mapping it is used #ManyToOne instead #OneToOne?

I am absolutly new in Hibernate development and I have the following problem.
I have 2 entity classes that maps 2 DB tables:
1) The first entity class (the main one) is named KM_ProjectInfo and map a DB table named KM_PROJECT.
2) The second entity class is named KM_ProjectInfoStatus and map a DB table named KM_PROJECT_INFO_STATUS.
So the second one represent a specific field of the first one (a status of the row representd by an instance of the KM_ProjectInfo class). Infact I have something like this:
1) KM_ProjectInfo class:
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
// GETTERS & SETTERS
}
2) KM_ProjectInfoStatus:
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
// GETTERS & SETTERS
}
So, as you can see in the previous snippet, the KM_ProjectInfoStatuss is a field of the KM_ProjectInfo because I want that it contains the primary key of this table as foreign key.
In the logic of my application I want that at one row of the KM_PROJECT table (so at one instance of the KM_ProjectInfo entity class) is associated a single row of the KM_PROJECT_INFO_STATUS (one instance of the KM_ProjectInfoStatus entity class) because it represent a specific status for the KM_PROJECT row.
In my code I have:
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
but I think that is wrong because at one row of my first table it is associated a specific single row of the second table. But maybe I am missing something about how Hibernate work.
Can you help me to understand what I am missing? What it work? Why I have #ManyToOne instead #OneToOne?
Tnx
It all depends on how you want to model things. In terms of Database structure, OneToOne and ManyToOne are implemented in the same way:
One or more JoinColumns which makes a foreign key pointing to the primary key of the other table.
So both solutions correctly map to your database, but it depends if you want to allow several KM_ProjectInfo to point to the same KM_ProjectInfoStatus, or only allow a single one.
Note that, even though you would declare a OneToOne, you could still end up with multiple KM_ProjectInfo pointing to the same KM_ProjectInfoStatus if you don't manipulate Hibernate properly.
Here you did not declare the reverse relationship, but if you did, the declaration would have to be different:
In case of a OneToOne, you would have a KM_ProjectInfo member
In case of a OneToMany (reverse of ManyToOne), you would have a Collection<KM_ProjectInfo> member
From the description it seems you want to have one-to-one relationship. That is the project entity should have its very own status not shared by any other project. You could achieve this by using #OneToOne as below.
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#OneToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
}
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#OneToOne(mappedBy="idProjectInfoStatus")
private KM_ProjectInfo project;
}
This way you can have specific status for the KM_PROJECT.
Coming back to #ManyToOne, you will want to have this if you want to share the same status with multiple projects, but that's not what you want in your case. I have tried to explain mappings in simple way here One-to-One mapping.

How can I join 3 entities JPA?

I have 3 entities: Aluno, Turma and Modalidade. Now I need create Matricula, this entity Matricula will contain all ids of Aluno, Turma and Modalidade with others attribute.
one Matricula can have one Aluno and can have many Turma and can have many Modalidade.
Entity Matricula, can have:
OneToOne Aluno
OneToMany Turma
OneToMany Modalidade
I hope can yours understand.
How to I do that ?
I have a tutorial that goes into a fair bit of detail about how you set up various relationships using Hibernate annotations. You can find it here.
I'm going to assume that you'd want bi-directional relationships using a foreign key mapping (as shown in the tutorial, if this is wrong, you can find the uni-directional configurations there), you can basically just declare your classes like this:
#Entity
#Table
public class Matricula {
#Id
private long matriculaId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "alunoId")
private Aluno aluno;
#OneToMany(mappedBy="turma")
private List<Turma> turmas;
#OneToMany(mappedBy="modalidade")
private List<Modalidade> modalidades;
}
#Entity
#Table
public class Turma {
//Put a unique ID here to be used as PK
#ManyToOne
#JoinColumn(name="matriculaId)
private Matricula matricula;
}
#Entity
#Table
public class Modalidade {
//Put a unique ID here to be used as PK
#ManyToOne
#JoinColumn(name="matriculaId)
private Matricula matricula;
}
#Entity
#Table
public class Aluno {
//Put a unique ID here to be used as PK
#OneToOne(mappedBy="aluno")
private Matricula matricula;
}
Please note that this is assuming that your column names will match, and that your database is correctly set up.
Hope it goes well

Java/Hibernate/JPA: cannot persist with compound key -> transient object

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.

Categories

Resources