JPA: Creating an entity whenever another entity is created - jpa

I'm using JPA/Hibernate over PGSQL DB.
I have an entity in my application, and I want to persist another entity (of a different type) every time the first entity is persisted. For example, whenever an "ORDER" is created, I want to immediately persist an empty "ORDER_INVOICE" entity and connect it to the order. These reside in two different tables.
At first I thought about writing a #PostPersist function for the ORDER entity and persisting the ORDER_INVOICE in it, but my problem is that I don't have the Entity Manager in this context.
I'm looking to avoid remembering to persist the ORDER_INVOICE upon every ORDER persistence.
Is that the right way to go? If so, how do I get the EM into the PostPersist? And if not, what would be a better way?

Why don't you simply create it in the constructor of your master entity, and set cascade=persist on the relationship?
#Entity
public class Order {
#OneToMany(mappedBy = "order", cascade=CascadeType.PERSIST)
private List<Invoice> invoices = new ArrayList<Invoice>();
public Order() {
Invoice i = new Invoice();
i.setOrder(this);
this.invoices.add(i);
}
// ...
}
EDITED :
To avoid creating a new invoice each time the Order's constructor is invoked (by JPA, for example), you could use this kind of code :
#Entity
public class Order {
#OneToMany(mappedBy = "order", cascade=CascadeType.PERSIST)
private List<Invoice> invoices = new ArrayList<Invoice>();
/**
* Constructor called by JPA when an entity is loaded from DB
*/
protected Order() {
}
/**
* Factory method; which creates an order and its default invoice
*/
public static Order createOrder() {
Order o = new Order();
Invoice i = new Invoice();
i.setOrder(o);
o.invoices.add(i);
}
// ...
}
If the order is persisted after having been instanciated by the factory method, then the invoice will be persisted as well (thanks to the cascade). If the order is not persisted, then it will be garbage collected at some point, and its default invoide as well.

Related

EF Core - many-to-many: how to add middle entity when one entity does not yet exist?

I am curios if I can make this easier/better.
The user is able to create new tickets and with each ticket can be several files associated, one file can be related to multiple tickets.
Now, when the user creates a ticket, he can already add files. Meaning I have no Id for the ticket and thus no way to build a relation. How should I solve this?
public class FilesPerTicket
{
public int TickedId;
public Ticket Ticket;
public int FileId;
public File File;
}
public class File
{
public ICollection<FilesPerTicket> FilesperTicket;
}
public class Ticket
{
public ICollection<FilesPerTicket> FilesperTicket;
}
Now the user creates the ticket and adds several files to it.
public IActionResult Create(MyModel model)
{
// .....
var filesPerTicket = model.Files.Select(x => new FilesPerTicket() { FileId = x.Value }).ToList();
var newTicket = new Ticket() { //...... };
newTicket.FilesPerTicket = filesPerTicket;
// ....
context.Add(newTicket);
context.SaveChanges();
}
This doesn't work because we haven't provided a Ticketid and therefore every TicketId in FilesPerTicket is 0.
I know that I just can save the ticket and afterwards it will have the primary key in it. Which I then can use to fill the FilesPerTicket and save that one.
But then I would have those in separate transactions (because SaveChanges was called) and use another try/catch.
Is there an way to tell EF Core to fill this TicketId automatically when this entity gets saved at the same time as a ticket? Or some other way to save that entity?

How to get navigation property of foreign key when calling SaveChanges()?

I'm using EF5 code first.
We have a method
LogHistoryTracking(DbEntityEntry entity)
to log changes when SaveChanges is called.
At SaveChanges, we get the changed entities and pass into LogHistoryTracking
var changedEntities = ChangeTracker.Entries().ToList();
But when I access
changedEntity.OriginalValues.PropertyNames
there is no properties for foreign keys object (only foreign key Id - but how can we get the data when there is only id here?).
I also tried to google for a solution, but this issue might be not so popular.
There is this article, but it does not work.
Appreciate any help. Thanks.
If you want to have your entity properties to be accessible you must 'Include' them prior to accessing them. Like in the following example which gets the orders of the first cutomer :
var orders = context.Customers
.Include("Orders")
.First().Orders;
In this example if you do not call .Include("Orders") you will not have Customer.Orders. The same goes if you have foreign key and forget to include the navigation property of the foreign key. This is because the key (the ID) is part of the object and the navigation property is not.
Let us see one real world example :
public class Employee : Entity
{
public virtual int CompanyUserId { get; set; }
public virtual CompanyUser CompanyUser { get; set; }
//... cut out for brevity
}
If you get the employees like this :
var employees = context.Employees;
You will not be able to access employees[0].CompanyUser after
context.SaveChanges() because of lazy loading. The connection is disposed after context.SaveChanges(), so no more data fetching.
But if you call :
var employees = context.Employees
.Include("CompanyUser")
.ToArray();
You will be able to access employees[0].CompanyUser.SomeProperty right away before context.SaveChanges regardless lazy loading because ToArray() will execute the query and fetch the entities with the "includes".
If you call :
var employees = context.Employees
.Include("CompanyUser");
Then you will have employee[0].CompanyUser.SomeProperty even after context.SaveChanges() with Lazy Loading because you have told EF to include "CompanyUser" property before executing the query. On execution EF will include the named property.
UPDATE
Intercepting DbContext can be done in at least two different ways.
First - override SaveChanges() or SaveChangesAsync because it is virtual:
public class MyDbContext : DbContext
{
public event Action<MyDbContext> SavingChanges = _ => { };
public override int SaveChanges()
{
this.SavingChanges(this);
return base.SaveChanges();
}
}
Second way without direct override is by hiding the DbContext inside interface like this one (this is from real project) :
public interface IUnitOfWork : IDisposable
{
void Commit();
}
Third way (somewhat different) is by intercepting the Db calls.
Fourth way exists but it depends on what IoC you use. If you use Castle Windsor you can use interceptors. I suppose that with every IoC there is its own way of intercepting this.

How to filter child entities collections with predicate?

I have an entity service on which I need to filter a collection of child entity, based on a list of id's. My service have a public method which receive the id of the parent entity and a list of id's of some of his children entities.
By default, I know that JPA will fetch all related entities and this his the actual behavior. But we need to work on the performance of the service. So instead of getting all related entities and filter them with many loop (filter on id's and also on other properties like date property), I want to get only entities concerned by my request.
My Parent entity
#Entity
#Table(name = "MyParent")
public class MyParentEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyParent")
#SequenceGenerator(allocationSize = 1, name = "SEQ_MyParent",
sequenceName = "SEQ_MyParent")
#Column(name = "ID_PARENT")
private Long id;
#OneToMany(mappedBy = "myParent", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true)
private final List<MyChildEntity> myChild = new ArrayList<MyChildEntity>();
}
My Child Entity
#Entity
#Table(name = "MyChild")
public class MyChildEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyChild")
#SequenceGenerator(allocationSize = 1, name = "SEQ_MyChild",
sequenceName = "SEQ_MyChild")
#Column(name = "ID_CHILD")
private Long id;
#ManyToOne
#JoinColumn(name = "ID_PARENT")
private MyParentEntity myParent;
}
I'm using Spring-data CrudRepository to get data from my DB and I also extends JpaSpecificationExecutor to use Predicate.
public interface MyParentRepository extends CrudRepository<MyParentEntity, Long>,
JpaSpecificationExecutor<MyParentEntity> {
}
This let me use CrudRepository findOne() method but with a Specification object instead of the regular Long parameter.
Also, I combine multiples Specification's object with the following call:
this.myParentRepository.findOne(Specifications
.where(firstSpecification(parentId))
.and(secondSpecification(childrenIdsList)));
I created a simple junit test with one Parent linked to two children entities. In my request, I'm able to get the parent entity with the provided Id. But even if I provide the child id, I always get both children entities in the list inside the parent.
In my method which return a new Specification object, in which the toPredicate method is override, I'm unable to create a Predicate that will filter my children collection and only get those one I'm interested. I know that the Hibernate Criteria has the possibility to add "Restrictions" but this is not available in the CriteriaBuilder that is provided with the toPredicate method.
public static Specification<MyParentEntite> firstSpecification(final Long id) {
return new Specification<MyParentEntite>() {
#Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.equal(root.get(MyParentEntity_.id), id);
return cb.and(predicate);
}
};
}
public static Specification<MyParentEntite> secondSpecification(final List<Long> ids) {
return new Specification<MyParentEntite>() {
#Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Root<MyChildEntity> child = query.from(MyChildEntity.class);
Expression<Long> exp = child.get(MyChildEntity_.id);
Predicate p = exp.in(ids);
return cb.and(p);
}
};
}
In the secondSpecification() method, I also tried to use ListJoin instead of Root directly in the Entity. I searched in other questions here but it seems that this concern is solved with the Hibernate Criteria restrictions or with a LeftJoin, which I tried in my ListJoin in specifing JoinType.LEFT parameter.
Here are links to already tested solutions whitout success :
JPA CriteriaBuilder - How to use "IN" comparison operator
JPA2 Criteria-API: select... in (select from where)
I want to mention that I'm relatively new with Criteria API and Predicate. Maybe I'm missing something that is simple but that is obvious to experienced JPA developpers!
Thanks a lot for your help!
Finally, I found a way to resolved my issue. Requesting only partial collections of sub-entities is something that we found dangerous in terms of data integrity. If a remote service calls to request my parent entity with a partial collection of children's entities within a get, this parent entity object may be return for a modify operation which will result in many "delete" calls on the removed instances of children entities. The persistence API will consider these missing children as relations that were removed, which is something we don't want.
I created a dummy transfert object which contains the partial collections of children's entities requested so this dummy transfert object can't not be use in a future modify operation call. The full version of the parent entity will be used for the "modify" purpose.
Is your JPA provider hibernate?Have you considered the filters in hibernate which can filter the child entities instead of removing them.But the filter usage is somehow very difficult to understand!

jpa: when merging many to many previous record gets deleted

i have a Users and Tags table,and also a user_tag_xref table that holds the many to many relationship.now netbeans generates the entity classes for me (using eclipselink) below is the entity mapping relationship
on User class
#ManyToMany(mappedBy = "usersList")
private List<Tags> tagsList;
on Tags class
#JoinTable(name = "USERS_TAG_XREF", joinColumns = {
#JoinColumn(name = "TAG_ID", referencedColumnName = "TAG_ID")}, inverseJoinColumns = {
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")})
#ManyToMany
private List<Users> usersList;
Now im my business logic RESTfull service,a json client consumes this method
#POST
#Path("/registration2/tag")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response registration2_3(List<Tags>tagList,#Context HttpServletRequest req){
Profile p =(Profile) registerMap.get(req.getSession().getId());
Users u = em.find(Users.class,p.getUserId());
for(Tags t : tagList){
t.getUsersList().add(u);
u.getTagsList().add(t);
em.merge(t);
em.merge(u);
}
logger.log(Level.INFO, "the taglist created for user");
return Response.ok(u,MediaType.APPLICATION_JSON).build();
}
The problem is each time i merge a new user to create a many to many relationship, if an existing userid=6201 has a tag with 2,3,4 ,and the new user is try to use to tag id 2,3,4,
the existing user is deleted and the new user merges to the tags. i have read several articles on overriding hash and equals to method in my entity classes,those methods are overridden by default in eclipselink ,also i cannot change my collection type to Set or Collection since List<> type works perfectly well for a json array. i v been having a hard time right now,its been 24hours,could it be the default mapping strategy is wrong? do i need to cascasde?
You have to be extra cautious while using merge as its semantics ( as explained here) is bit different from just update.
As your relationship is bidirectional and users is the inverse side, so all of relationship persistence will be handled by
tags side. Assuming that you tag lists contains detached tags, meaning all Tags have their id set, then you need to iterate over tagList
Tag managedTag = em.merge(t);
This takes care that if t is new instance (unmanaged) then a persistent representation of it will be returned
which has to be used there after or if the instances were having their id set, then the ORM will create a managed instance with data from database ( or from first/second level cache if it exists there). The returned instance is the one managed
for(Tags t : tagList){
Tag managedTag = em.merge(t);
managedTag.getUsersList().add(u);
u.getTagsList().add(t);
User managedUser = em.merge(u);
}
Also you can set the Merge cascade option on the Tag side to save you the second merge call and let the ORM manage relationship automatically.
Here is how merge behaves with detached entities and relations.
#Entity
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(cascade=CascadeType.ALL)
private List<User> users = new ArrayList();
.......................
}
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(mappedBy="users",cascade=CascadeType.ALL)
private List<Tag> tags = new ArrayList();
........................
}
TestProgram
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Test");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
User user = new User();
user.setName("User");
User managedUser = em.merge(user);
int userId = managedUser.getId();
//create a tag
Tag tag = new Tag();
tag.setName("Tag");
tag.addUser(managedUser);
Tag managedTag = em.merge(tag);
//save the id locally
int savedId = managedTag.getId();
//managed tag was sent to UI where its name will be changed to "Changed Tag"
em.getTransaction().commit();
em.close();
//create another transaction
EntityManager em1 = emf.createEntityManager();
em1.getTransaction().begin();
//simulate a tag sent form UI
Tag tagFromUI = new Tag();
tagFromUI.setId(savedId);
tagFromUI.setName("Changed Tag");
// I want to associate a new user to this tag
// so create a new user
User newUser = new User();
newUser.setName("newUser");
tagFromUI.addUser(newUser);
Tag managedTagFromUI = em1.merge(tagFromUI);
em1.getTransaction().commit();
em1.close();
emf.close();
Here is the SQL generated and corresponding explanation
//First transaction begins
insert into User (name) values ('User');
insert into Tag (name) values ('Tag');
insert into Tag_User (tags_id, users_id) values (1, 1);
//First transaction ends
//Second transaction begins
// Since a detached tag is merged, hibernate queries for tag id 1 to load it in persistent context
//This tag is associated with a user
select tag0_.id as id1_3_1_, tag0_.name as name2_3_1_, users1_.tags_id as tags_id1_3_3_, user2_.id as users_id2_4_3_, user2_.id as id1_5_0_, user2_.name as name2_5_0_ from Tag tag0_ left outer join Tag_User users1_ on tag0_.id=users1_.tags_id left outer join User user2_ on users1_.users_id=user2_.id where tag0_.id=1;
//copies the state of detached tag from UI to the managed tag and sends update
update Tag set name='Changed Tag' where id=1;
//since merge is cascaded, hibernate looks for the user list of supplied tag and sees an transient User
// the transient instance is merged (created new in database as it is not yet persisted)
insert into User (name) values ('newUser');
// merge is called on this new managed instance and the resulted instance is set in the managed Tag instance automatically
//but for this the old relation has to be broken
delete from Tag_User where tags_id=1;
// and the new relation has to be added in database
insert into Tag_User (tags_id, users_id) values (1, 2);
//second transaction ends
while Adding new entries you need to validate that the mappings are not duplicate or already present in the db.For more clarity on the solution plz take a look into my soultion provided in the link
Relationship table data overwritten in many-to-many hibernate

JPA not updating ManyToMany relationship in returning result

Here are my entities:
#Entity
public class Actor {
private List<Film> films;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="actor_id"),
inverseJoinColumns = #JoinColumn(name="film_id"))
public List<Film> getFilms(){
return films;
}
//... more in here
Moving on:
#Entity
public class Film {
private List actors;
#ManyToMany
#JoinTable(name="film_actor",
joinColumns =#JoinColumn(name="film_id"),
inverseJoinColumns = #JoinColumn(name="actor_id"))
public List<Actor> getActors(){
return actors;
}
//... more in here
And the join table:
#javax.persistence.IdClass(com.tugay.sakkillaa.model.FilmActorPK.class)
#javax.persistence.Table(name = "film_actor", schema = "", catalog = "sakila")
#Entity
public class FilmActor {
private short actorId;
private short filmId;
private Timestamp lastUpdate;
So my problem is:
When I remove a Film from an Actor and merge that Actor, and check the database, I see that everything is fine. Say the actor id is 5 and the film id is 3, I see that these id 's are removed from film_actor table..
The problem is, in my JSF project, altough my beans are request scoped and they are supposed to be fetching the new information, for the Film part, they do not. They still bring me Actor with id = 3 for Film with id = 5. Here is a sample code:
#RequestScoped
#Named
public class FilmTableBackingBean {
#Inject
FilmDao filmDao;
List<Film> allFilms;
public List<Film> getAllFilms(){
if(allFilms == null || allFilms.isEmpty()){
allFilms = filmDao.getAll();
}
return allFilms;
}
}
So as you can see this is a request scoped bean. And everytime I access this bean, allFilms is initially is null. So new data is fetched from the database. However, this fetched data does not match with the data in the database. It still brings the Actor.
So I am guessing this is something like a cache issue.
Any help?
Edit: Only after I restart the Server, the fetched information by JPA is correct.
Edit: This does not help either:
#Entity
public class Film {
private short filmId;
#ManyToMany(mappedBy = "films", fetch = FetchType.EAGER)
public List<Actor> getActors(){
return actors;
}
The mapping is wrong.
The join table is mapped twice: once as the join table of the many-to-many association, and once as an entity. It's one or the other, but not both.
And the many-to-many is wrong as well. One side MUST be the inverse side and use the mappedBy attribute (and thus not define a join table, which is already defined at the other, owning side of the association). See example 7.24, and its preceeding text, in the Hibernate documentation (which also applies to other JPA implementations)
Side note: why use a short for an ID? A Long would be a wiser choice.
JB Nizet is correct, but you also need to maintain both sides of relationships as there is caching in JPA. The EntityManager itself caches managed entities, so make sure your JSF project is closing and re obtaining EntityManagers, clearing them if they are long lived or refreshing entities that might be stale. Providers like EclipseLink also have a second level cache http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching