Spring Hibernate Relation Mapping - spring

I'm looking for help in one of my project.
I'm having Company Class and Bank Class.
Company Class and Bank Class are to be mapped using Many to Many Relation using Hibernate.
How Do I start? I'm done creating Company Module which is inserting data into table and same for the Bank. But How to show the mapping between both?
Flow Goes like this -
Add Company -> Edit/Update -> Add Bank to the previous Company Detail -> Bank Also Add/Update -> View All, which is needed to show The list of Companies and their respective banks.

I assume you use junction table so i would done it like this:
Company.class
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "nameOfJunctionTable", catalog = "yourDatabaseName", joinColumns = {
#JoinColumn(name = "companyId", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "bankId",
nullable = false, updatable = false) })
private Collection<Bank> banks;
Bank.class
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "banks")
private Collection<Company> companies;

Related

Query result Infinite Recursion on ManyToMany relationship on hibernate

I have a entity mapping like this:
As you can see it is the bidirectional relationship and the team_users table has it own primary key and extra column - active.
Key code in team entity:
#OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in user entity:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
Key code in team_user entity:
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
I have a API which will return all team information, then I have service class like:
#Autowired
private TeamRepository teamRepo;
public List<Team> listAll() {
return (List<Team>) teamRepo.findAll();
}
Then the chrome log me that I have a "undefined" value, and when I test it by postman it shows me the infinite loop value:
I want to figure out what is the best approach to fetch the data?
I want to all information that tells me the team situation, including team... users...active status , almost everthing.
Any suggestions?
update
I tried to use #JsonIgnore on intermidate table :
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "TEAM_ID")
private Team team;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
but I won't get user information in that case and it avoid infinite loop:
What else I can do to get all information for teams?

Spring Boot + JPA (Hibernate) - how to update some fields

I have to update my entity - when placeA or/and placeB is updated, I have to also update points.
SO I fetch route object from database and modify one or two fields (placeA, placeB). The problem is that I have to update points accordingly -> in Point object I have to also update pointAddress (point.pointAddress has to be updated to route.placeA or route.placeB values):
public class Route{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_a_id")
private Address placeA;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_b_id")
private Address placeB;
#OneToMany(mappedBy = "route", cascade = CascadeType.ALL)
List<Point> points;
public class Point{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "point", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<PointDetail> pointDetails;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_id", nullable = false)
private Route route;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_address_id", nullable = false)
private Address pointAddress;
public class PointDetail{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "point_id", nullable = false)
private Point point;
And I fetch Route entity from db and from now this object is in persistent state (everything within the same transaction) so I don't need to invoke repository.save(myChangedRoute) explicitly.
The question is how to update route.points[0].pointAddress?
Is it sufficient?
Route route = repository.findRouteById(1L);
route.setPointA("new place");
route.getPoints().get(0).setPointAddress("new place")
And what with route.getPoints().get(0).getRoute() and route.getPoints().get(0).getPointDetails() objects? For example should I also update route.getPoints().get(0).getPointDetails() object? PointDetail objects have a field point that maybe should be also updated?
I have related (nested) objects in my Route object (dependencies) so my question is how to update my object structure properly to not overwrite new values with the old nested ones that are not updated, e.g. I updated:
route.getPoints().get(0).setPointAddress("new place")
but I haven't updated route.getPoints().get(0).getPointDetails().get(0).setPoint(MY NEW UPDATED AND NOT YET SAVED Point object)???
SO we have a circular dependencies route -> point -> pointDetail -> point and the question is if it's sufficient to update only pointAddress in my route.point object or I have to also update pointAddress in route.point.pointDetail.point?
First of all: point -> pointDetail -> point is not a curricular dependecny. The relation between pointDetail and point is BiDirectional dependency.
PointDetail objects have a field point that maybe should be also updated? Of course not.
and the question is if it's sufficient to update only pointAddress in my route.point? Yes that is enough.
Hibernate has the first level cache it ensures that objects will be loaded only once per session. So no override can occur. Of course, is up to you and your code how will you update properties and in which order.

Hibernate insert causes update of another table

I have a model that looks like this:
#Entity
#Table(name = "A")
class A {
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "A_categories", joinColumns = {
#JoinColumn(name = "A_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "category_id",
nullable = false, updatable = false) })
private List<Category> categories;
}
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
So there is a many-to-many relationship between A and Category. Now categories are static, and cannot be changed by a user. From the UI, the user will try to save an entity A, and each can have one or more categories. So the JSON that comes back looks a little like this:
{A: {categories: [{id: 1}, {id: 2}, {id: 3}]}}
Now when I try to save this A object (after jackson has unmarshalled to java), I just want entries to be made in the join table, A_categories, for each category the new entity has.
However, the Category entity itself also gets updated. So if you notice, the JSON does not have any category_name, and so the database entry for each Category will also get updated to a null entry for the name.
How can I prevent this from happening?
Two different approaches:
1) Set managed categories before merging.
a.setCategories(readAllByIds(a.getCategories()))
private Collection<Category> readAllByIds(Collection<Category> categories) {
Collection<Category> result = new ArrayList();
for (Category category : categories) {
result.add(entityManager.getReference(Category.class, category.getId()));
}
return result;
}
EntityManager.getReference returns proxy, so the additional benefit is that no database round trips are executed for reading the associated categories.
With this solution you are not merging the deserialized categories into the persistence context, thus Hibernate will not synchronize their state with the database.
2) Do not cascade any operations from A to categories (remove cascade attribute).
This way, neither PERSIST nor MERGE will be cascaded and Hibernate will just use ids of the detached Category instances to store the data into the relationship table.
Sidenote: Generally, cascading REMOVE or ALL in a many-to-many association makes no sense (if you remove an A you probably don't want to remove all the categories it belongs to).
#Column has the attributes insertable and updatable. You can set them to false:
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id", insertable=false, updatable=false)
#GeneratedValue
private Integer id;
#Column(name = "category_name", insertable=false, updatable=false)
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private List<A> a;
}
You can try adding this
#Entity
#Table(name = "category")
class Category {
#Id
#Column(name="id")
#GeneratedValue
private Integer id;
#Column(name = "category_name")
private String categoryName;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories", cascade=CascadeType.DETACH)
private List<A> a;
}
with the cascade.DETACH should not save changes when you save A entity, but let me know if is not working to make an example modifying the ManyToMany relationship with this DETACH action

JPA many to many - do i have to remove\add from both collection sets?

I have a User entity,
And a Department Entity
i want to have #manyToMany relationship between them :
many users can have many departments.
in my User entity:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "UserDepartments", joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "department_id", referencedColumnName = "id") })
private Set<Department> departments;
and my Department entity has a SET as well of users..
my question is:
if i need to implement the method :
public void removeUserFromDepartment(User user, Department department) {
//bla bla
}
do i have to call
department.getUserCollection.remove(user);
AND
user.getDepartmentCollection.remove(department);
Or is there a way to maintain this logic by only removing one of them ?
If i have to save both its pretty hard to maintain especially for someone who doesn't know about the many to many relation of the two entities..
When a OneToMany or ManyToMany relationship exists in JPA the client code is responsible for managing the relationship. This means that you must explicitly remove the object from both sides of the relationship.
So lets say you have a User instance and need to remove a department.
User user = dao.findUser(1L); //Find a user, psuedo code
Department dept = user.getDepartments().get(0);
dept.getUsers().remove(user);
user.getDepartments().remove(user);
dao.update(user); //once again psuedo code
To use the code above you should add a cascade type to the relationship in the user entity:
#ManyToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL)
#JoinTable(name = "UserDepartments", joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "department_id", referencedColumnName = "id") })
private Set<Department> departments;
This will cause saves on the User entity to be cascaded to the Departments entity. Just a reminder save is psuedo code, it will boil down to a call on the EntityManager such as persist or merge.

JPA Self Join using JoinTable

I have 1 entity call Item in which I want to be able to link parent items to children. to use a join table to create a parent/child relationship. I haven't been able to get any good documentation on. So if anyone has any thoughts I'm all ears.
Here is what I have... which works most of the time.
public class Item implements java.io.Serializable {
#Id
private Long id;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinTable(name = "ITEMTOITEM", joinColumns = { #JoinColumn(name = "ITEMID") }, inverseJoinColumns = { #JoinColumn(name = "PARENTITEMID") } )
private Item parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Item> children;
}
At times when I want to bring back objects that are tied to this item table I am getting an error of the following:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.webflow.engine.ActionExecutionException: Exception thrown executing [AnnotatedAction#6669ff5 targetAction = com.assisted.movein.web.common.nav.NavAction#6edf74b7, attributes = map['method' -> 'handleEntry']] in state 'oneTimeChargesAndFeesView' of flow 'in-flow' -- action execution attributes were 'map['method' -> 'handleEntry']'; nested exception is Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2.0.1 (Build b04-fcs (04/11/2008))): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: ORA-00904: "PARENTITEM_ITEMID": invalid identifier
Error Code: 904
Call: SELECT ITEMID, ITEMSHORTDESC, EFFENDDATE, ITEMDESC, PARENTITEM_ITEMID, ITEMTYPECODE FROM ITEM WHERE (ITEMID = ?)
bind => [1250]
Query: ReadObjectQuery(com.domain.Item)
Any help would be appreciated.
Try to use #JoinColumninstead:
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "PARENTITEMID", referencedColumnName = "ITEMID")
private Item parent;
#OneToMany(
cascade = {CascadeType.ALL},
orphanRemoval = true,
fetch = FetchType.LAZY
)
#JoinColumn(name = "PARENTITEMID")
private List<Item> children;
After a lot of reading on JPA 2.0 I figured out that I needed a newer version of Eclipselink 2.3+ in order to support what I was trying to do. Here is the code that actually worked in my case, but it will not work due to our dependency on an older version [EclipseLink 2.0.2]. Also another project's is still using Toplink-essentials JPA 1.0 which again does like this notation.
public class Item {
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinTable(name = "ITEMINCENTIVESMAPPING",
joinColumns = { #JoinColumn(name = "INCENTIVEITEMID", referencedColumnName = "ITEMID", insertable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "ITEMID", referencedColumnName = "ITEMID", insertable = false, updatable = false) } )
private Item parentItem;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "ITEMINCENTIVESMAPPING",
joinColumns = { #JoinColumn(name = "INCENTIVEITEMID", referencedColumnName = "ITEMID") },
inverseJoinColumns = { #JoinColumn(name = "ITEMID", referencedColumnName = "ITEMID") } )
private List<Item> items;
}
I believe #JoinTable is only allowed on a #OneToOne or #OneToMany or #ManyToMany, I don't think it makes any sense on a #ManyToOne. Use a #JoinColumn, or a #OneToOne.
Also your #OneToMany does not make sense, the mappedBy must be an attribute and you have no parentItem, it should be parent, and parent should use a #JoinColumn.

Resources