I have one reletionship many to one.
Many cars have one owner. How delete the owner only if all of his cars deleted?
Ex one owner have two cars.
1)delete first car nothing happens to owner. delete second car delete owner.(Can be achieved with orphanremoval=true?)
2) delete owner delete all his car. (Can be achieved with CascadeType.Remove ?)
class Car{
#ManyToOne(orphanremoval=true)
private Owner owner;
}
class Owner{
#OneToMany(cascade= CascadeType.REMOVE)
private List<Car> cars;
}
Related
Having the following kind of table tables. What will be the good approach to persist these tables? Used inheritance strategy for this, but it didn't work as expected.
Requirement 1: Need to persist student table, it will persist the member as well as address table as well
Requirement 2: Need to persist teacher table, it will persist the member as well as address table as well
Need to perform get, update and delete option on these tables.
Member {
member_id - have one to one relation with student id and teacher id
lastupdateddate
latupdatedby
}
Student {
student id - have one to one relation with member id
student name
lastupdateddate
latupdatedby
}
teacher {
teacher id - have one to one relation ship with member
teacher name
lastupdateddate
latupdatedby
}
address {
address id
member_id - have one to one relationship with member class
lastupdateddate
latupdatedby
}
When I persist/update student details, the address related info is not properly inserted or updated.When I check insert queries fired on member, then student table after on address table. But, in the insert query to address table, the member_id value is coming as null.Because of this only address table is not populated.
Entity structure is is as given below
public abstract class Member implements Serializable {
}
public class Student extends member implements Serializable {
}
public class Teacher extends member implements Serializable {
}
public class Address implements Serializable {
}
The mapping is mentioned as given below. Tried out various available options.
In member entity class
#Access(AccessType.PROPERTY)
#OneToOne(mappedBy="member", cascade=CascadeType.ALL)
public Address getAddress() {
return postalAddress;
}
In address entity class
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "MEMBER_ID")
private Address address;
It looks like you have 2 options:
InheritanceType.JOINED. It's almost what you've described: common part is in one table, different parts are in different tables. On each request JOIN will occure
InheritanceType.SINGLE_TABLE. Here all the data will be stored in single table and descriminator will be used to determine if record if for student or for teacher.
Personally I would prefer second options because you have almost all fields in common, also most of operations are lighter and involve WHERE, not JOIN.
I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter
Maybe this is a question with an easy answer ... but I don't get it running. At persist() I get the exception that the referential key in the child table is null (which of course is not allowed by the database). I have a recipe and some steps for preparation.
I'm using EclipseLink 2.4.1
Recipe.java (rcpid is autoset by JPA)
#Entity
public class Recipe {
#Id
long rcpid;
List<Recipestep> recipesteps = new ArrayList<>();
#OneToMany(
cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="recipe",
targetEntity=Recipestep.class )
// This does NOT work. Following line tries to access a join-table !!!
// #JoinColumn(name="rcpid", referencedColumnName="rcpid")
public List<Recipestep> getRecipesteps() { return recipesteps; }
// some more attributes, getters and setters
}
Recipestep.java (rpsid is autoset by JPA)
#Entity
public class Recipestep {
#Id
long rpsid;
Recipe recipe;
#ManyToOne( targetEntity=Recipe.class )
#JoinColumn( name="rcpid" )
public Recipe getRecipe() { return recipe; }
// some more attributes, getters and setters
}
The code above is a valid workaround. However to have clean (and supportable) code, the relationship should be only one-way with a collection in the parent which references all its children.
You have mapped this as a unidirectional one to many, but have two mappings for the recipestep rcpid database column. Try changing the long rcpid to
#ManyTOne
Recipe rcp;
And then remove the joincolumn definition from the oneToMany and make it bidirectional by marking it as mappedby the rcp manyToOne relation. An example is posted here http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/OneToMany
Eclipselink will always insert nulls on unidirectional oneToMany relations using a joincolumn when first inserting the target entity, and then update it later when it processes the Recipe entity. Your rcpid mapping in Recipestep is also likely null, which means you have two write able mappings for the same field which is bad especially when they conflict like this.
You are experiencing the default JPA behaviour. Adding an entity to the recipesteps list is not sufficient to create a bidirectional relation.
To solve the issue you need to set the rcpid explicitly on every element in the list.
EDIT: I think the issue is that JPA does not know where to store the id of the Recipe in the Recipestep table. It assumes a name ("recipebo_rcpid"), but your table seems to lack it.
Try adding the column "recipe_id" to the Recipestep table and a mappedBy attribute to the #OneToMany annotation:
#OneToMany(
cascade=CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "recipe" )
You probably do not need the targetEntity attribute in the annotation- the List is typed already.
I have a REST interface for a datamodel that has several one-to-many and many-to-many relationships between entities. While many-to-many relationships seem easy to manage statelessly, I'm having trouble with one-to-many. Consider the following one-to-many relationship:
Employee:
#ManyToOne
#JoinColumn(name = "Company_id")
private Company company;
Company:
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval=true)
public Set<Employee> employees = new HashSet<Employee>();
When a company is updated, its employee collection may have been updated as well (employees removed or added) but since the REST interface only allows updating the company as a whole, I cannot explicitly delete or add employees.
Simply replacing the collection does not work, but I found that this seems to work:
public void setEmployees(Set<Employee> employee) {
this.employees.clear(); // magic happens here?
this.employees.addAll(employees);
for (Iterator<Employee> iterator = employees.iterator(); iterator.hasNext();) {
Employee employee = (Employee) iterator.next();
employee.setCompany(this);
}
}
Is this the way it should be done, or is there a better way?
EDIT: In fact the above does not work! It appears to work at first, but then it will break with:
Exception in thread "main" java.lang.IllegalStateException: An entity copy was already assigned to a different entity.
I assume this happens because the db already contains a set of employees and if any of the "old" employees are also part of the replacement set, they collide with the ones in the database.
So what is the right way to replace the set?
First make sure equals is implemented properly. As per hibernate spec: http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch04.html#persistent-classes-equalshashcode
I had a similar problem doing a merge. Essentially I had to fetch the existing employees associated with the company. I had to merge any changes to existing employees, and then add any new employees.
Query query = em.createQuery("select e from Employee e where e.company = '" + company.getId() + "'");
Collection<Employee> existingEmployees = new LinkedList<Employee>();
try{
Iterables.addAll(existingEmployees, (Collection<Employee>) query.getResultList());
}
catch(NoResultException nre){
//No results
}
for(Employee existingEmployee : existingEmployees){
for(Employee employee : company.getEmployees()){
if(existingEmployee.name().equals(employee.name())){
employee.setId(existingEmployee.getId());
}
employee.setCompany(company);
}
}
i think you have no better choice then to replace the existing collection and simply set the new one provided by the REST response.
these are my simplified entities:
public class User : Entity
{
public virtual ICollection<Role> Roles { get; set; }
}
public class Role : Entity
{
public virtual ICollection<User> Users { get; set; }
}
var user = dbContext.Set<User>().Find(id);
dbContext.Set<User>().Remove(user);
dbContext.SaveChanges(); // here i get error (can't delete because it's the
//referenced by join table roleUsers
the problems is that the join table references the user table and ef doesn't remove the records from the join table before deleting the user
I tried writing test cases and I noticed that:
if use the same context to add user with roles, save changes, remove and again save changes it works
but if I use 2 different contexts one for insert and another one for delete I get this error
You must first clear Roles collection (user's roles must be loaded) before you will be able to remove user.
If you want to just get this deleting just do what the error is saying.
as a part of your DELETE method, you should do this, in order.
1) Get User including its related roles you can user User.Include(r=>r.roles)
2) iterate through and delete the roles for the given user (make sure you use a toList() when you do this loop )
3) Delete the user
4) savechanges
user.Roles
.ToList()
.ForEach(role => user.Roles.remove(role));
context.Users.remove(user);
context.SaveChanges();