How to swap #JsonBackReference and #JsonManagedReference based on which Entity is reference - java

I'm trying to find a way to swap the #JsonBackRefence and the #JsonManagedReference based on what entity I reference from the associated repository.
Site.java
#Entity
#Table(name = "Site")
public class Site {
#Id
private String id;
#OneToMany(mappedBy="site")
#JsonManagedReference
private List<Building> buildings;
}
Building.java
#Entity
#Table(name = "building")
public class Building{
#Id
private String id;
#ManyToOne
#JoinColumn(name = "SITE_ID")
#JsonBackReference
private Site site;
}
SiteRepository.java
public List<Site> findAll(); //Works as intended
BuildingRepository.java
public Building findById(buildingId); //Works if references are swapped
However when calling findById(buildingId), I want to have the #JsonBackReference swapped. Therefore, the #JsonBackReference is in Site.java and the #JsonManagedReference is in the Building.java entity.
Note: #JsonIdentityInfo almosts handles it, but it gives me too much information ie: when I call findById(buildingId) from the BuildingRepository it gives me all the buildings for the site joined to the building found.

If I understand you correctly the #JsonIgnoreProperties annotation should help you:
#JsonIgnoreProperties("site")
#OneToMany(mappedBy="site")
private List<Building> buildings;
#JsonIgnoreProperties("buildings")
#ManyToOne
private Site site;

Related

Hibernate remove just the relation, not the entity on the relation

I have a one to many relation on post class, and on the relation table I have one to one relation with user. Everything works find, but i want to be able to remove the relation, keeping the user entity, is that possible?
At this moment with the annotation orphanRemoval = true when I remove from post Detail list an element, this its removed from post_details table but the user is removed too.
#Entity
#Table(name = "ta_post")
public class Post{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Date fcDate;
#OneToMany(mappedBy="post", cascade=CascadeType.ALL, orphanRemoval = true)
private List<PostDetails>;
}
#Entity
#Table(name = "ta_user")
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int mail;
}
#Entity
#Table(name = "ta_post_details")
public class PostDetails{
private int id;
#ManyToOne
#JoinColumn(name="post_id")
private Post post;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="user_id")
private User user;
private String postComments;
}
You must remove the CascadeType.ALL from the PostDetails. If you want to be able to change the User through the PostDetails, you can set the CascadeType to PERSIST or MERGE. If you want to create a PostDetail along with an User, you need to include the CascadeType CREATE.
I'd guess you are creating the user somewhere else and you just associate one with a Post, so removing the CascadeType.ALL should be enough to not delete your User from the database.

JPA - 2 #ManyToMany relationships between 2 entities (one of them with extra Entity)

I am trying to solve JPA problem. I have 2 main entities - CameraItem and Chain (which represents ordered list of cameras)
Now there have to be 2 #ManyToMany relationships between CameraItem and Chain.
Each CameraItem has at least one parent Chain. As one CameraItem can belong to different Chains, and each Chain can have multiple CameraItems this is the first simple direct #ManyToMany relationship.
Chains can be connected with each other via CameraItem. In other words, CameraItem is holding the connection between Chains. But this is not simple #ManyToMany relationship, because we also need information about direction of the Chains connection. So it is #ManyToMany relationship with new Entity as Baeldung describes here https://www.baeldung.com/jpa-many-to-many. Entity ConnectionPoint is holding the information about the direction as a String.
I paste the classes here:
CHAIN CLASS:
#Entity
#Table(name = "chain")
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotBlank(message = "Chain name is mandatory")
private String name;
#Column(name = "PLANT_NAME")
private String plantName;
private String description;
private String status;
private Boolean hasPlant;
#CreationTimestamp
#Column(name = "creation_time")
private LocalDateTime creationTime;
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
#OneToMany(mappedBy = "chain")
Set<ConnectionPoint> connectionPoints;
CAMERA ITEM CLASS:
#Entity
#Table(name = "camera_item")
public class CameraItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn
private Camera camera;
private String name;
private Integer positionInChain;
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY)
private List<Chain> parentChainIds;
#OneToMany(mappedBy = "cameraItem")
Set<ConnectionPoint> connectionPoints;
CONNECTION POINT CLASS:
#Entity
#Table(name = "connection_point")
public class ConnectionPoint {
#Id
private Long id;
#Column(name = "direction")
private String direction;
#ManyToOne
#JoinColumn(name = "chain")
private Chain chain;
#ManyToOne
#JoinColumn(name = "camera_item")
private CameraItem cameraItem;
When I run the application I get this error:
org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:
no.trafsys.videodashboard.model.entity.CameraItem.camera_item in
no.trafsys.videodashboard.model.entity.Chain.cameraItems
Does somebody know where the problem can be?
I use #OneToMany annotations in Chain and CameraItem entities and #ManyToOne in ConnectionPoint like Baeldung in his tutorial.
Thank you in advance for any help
I don't think there is issue in ConnectionPoint. I think the issue is that:
In Chain class,
#OneToMany(mappedBy = "camera_item") // One-to-Many defined here
private List<CameraItem> cameraItems = new ArrayList<>();
while in CameraItem class, corresponding property is defined as follow:
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY) // Many-To-Many
private List<Chain> parentChainIds;
Try changing the mapping type to #ManyToMany in Chain class as well. It might work.
PS: I am not entirely sure of this, but this feels like the issue[incorrect mapping type]. Wanted to add this as a comment, but due to space issues, adding it as an answer.
#Entity
#Table(name = "chain")
public class Chain {
//..
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
//..
}
mappedBy parameter can only be in one side of the relation. I suspect camera_item is database table column name. So your cameraItems needs #JoinTable(name = "camera_item"... annotation

Multi-level sub-menu JSON in Spring boot

I have a some records in the table having parent child relations, screenshot below:
How do I write a JPA Entity to retrieve those records with respect to Parent-Child relation. Your help is appreciated.
The code that did not help me well is as below:
#Entity
#Table(name = PlatformConstant.TABLE_MENU)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "url", nullable = false)
private String url;
#Column(name = "description")
private String description;
#Column(name = "qr_code")
private Blob qrCode;
#JsonManagedReference
#OneToMany(mappedBy = "parent")
private Set<Menu> children;
#ManyToOne
#JsonBackReference
private Menu parent;
}
My code above has the following wrong output:
Using the JpaRepository find all, and applying the answer from #lucid, the new output is as below:
the code:
#Autowired
private MenuService menuService;
#CrossOrigin
#GetMapping("/all")
#ResponseBody
public List<Menu> getMenus() {
return (List<Menu>) menuService.findAll().stream()
.filter (menu-> Objects.isNull(menu.getParent()).collect(Collectors.toList()));
}
the output:
Thank you.
Jackson provides these annotations to control the parent-child relationships.
#JsonBackReference: skips property during the serialization process
#JsonManagedReference: forward reference and serialized annotated property
In your case, you don't want parent object to be serialized inside your child reference, you can annotate it with #JsonBackReference
#JsonManagedReference
#OneToMany(mappedBy = "parent")
private Set<Menu> children;
#ManyToOne
#JsonBackReference
private Menu parent;
Now, to remove child objects from response, we can filter that
Like this
menuService.findAll().stream()
.filter(menu-> Objects.isNull(menu.getParent()))
.collect(Collectors.toList());

Saving two entities at once with OneToOne relations

I've been trying to solve this thing for along time now, but I haven't gotten anywhere. I've been trying to save a entity that posses a reference to another entity.
User creates an place entity by filling out the form then presses save to save it. It should automatically make new rows into 'places' and 'place_urls' tables. Here is a link to SQL file that I'm loading into the application: https://pastebin.com/x8Gvk7ub
Parent entity:
#Entity
#Table(name="places")
public class Place {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", nullable=false, updatable=false)
private Long id;
#Column(name="userId")
private Long userId;
#Column(name="name", nullable=false, unique=true)
private String name;
#Column(name="address", nullable=false)
private String address;
#Column(name="largeDescription", nullable=false)
private String largeDescription;
#Column(name="smallDescription", nullable=false)
private String smallDescription;
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="id")
private PlaceUrl placeUrl;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="id")
private List<Booking> bookings;
getters and setters...
}
Child entity:
#Entity
#Table(name="placeUrls")
public class PlaceUrl {
#Id
#Column(name="id", nullable=false, updatable=false)
private Long id;
#Column(name="placeId", nullable=false, updatable=false)
private Long placeId;
#Column(name="url", nullable=false, updatable=true, unique=true)
private String url;
#OneToOne
#JoinColumn(name="id", referencedColumnName="placeId")
private Place place;
getters and setters...
}
Controller:
#PostMapping("/place/add")
public String addPlace(#ModelAttribute Place place, #AuthenticationPrincipal UserDetails currentUser) {
User user = userRepository.findUserByUsername(currentUser.getUsername());
place.setUserId(user.getId());
placeRepository.save(place);
return "redirect:/places";
}
Hibernate naming is set to implicitic-strategy in application.properties
UPDATE:
Place entity:
#OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="place")
private PlaceUrl placeUrl;
PlaceUrl entity:
Removed the placeId column, placeId variable and it's getters and setters.
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="placeId")
private Place place;
Controller changes:
#PostMapping("/place/add")
public String addPlace(#ModelAttribute Place place, #AuthenticationPrincipal UserDetails currentUser) {
User user = userRepository.findUserByUsername(currentUser.getUsername());
place.setUserId(user.getId());
place.getPlaceUrl().setUrl("something_nice");
placeRepository.save(place);
return "redirect:/places";
}
Now upon save I get: No message available java.lang.NullPointerException
UPDATE 2:
I got working by just messing around. I have no idea why it works, so someone else can explain.
#PostMapping("/place/add")
public String addPlace(Model model, #ModelAttribute Place place, #AuthenticationPrincipal UserDetails currentUser) {
PlaceUrl placeUrl = new PlaceUrl();
User user = userRepository.findUserByUsername(currentUser.getUsername());
placeUrl.setUrl(place.getName());
place.setPlaceUrl(placeUrl);
place.setUserId(user.getId());
placeUrl.setPlace(place); <-- this line here made it all work
placeRepository.save(place);
return "redirect:/places";
}
I followed the instructions of the guide book that I was suggested. Basically I added #OneToOne(mappedBy="place") to the parent then I added #OneToOne and #JoinColumn(name="placeId") to the child entity.
You're already mapping the relationship in parent entity but not using it in child. Instead, by defining another mapping, you cannot take advantage of cascading. The solution would be to add mappedBy in PlaceUrl:
#OneToOne(mappedBy = "placeUrl")
private Place place;
Or even better, use mappedBy on the child side, which is the clean approach.

JPA Bidirectional Onetomany json endless loop

the program under this particular environment:
EJB3.0 + JPA + jersey Web Service
First Entity :
#Entity
#Table(name = "student_by_test_yao")
public class StudentTest implements Serializable {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name = "class_id")
private ClassTest classes;
public StudentTest() {}
}
Second Entity:
#Entity
#Table(name = "class_by_test_yao")
public class ClassTest implements Serializable{
#Id
#GeneratedValue
private Integer id;
private String name;
#OneToMany(mappedBy = "classes",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<StudentTest> students;
public ClassTest() {}
}
When I get the ClassTest 's students list.
Exception is:
com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)
If I change the fetch FetchType.LAZY the Exception is:
org.hibernate.LazyInitializationException:
failed to lazily initialize a collection of role:
cn.gomro.mid.core.biz.goods.test.ClassTest.students,
could not initialize proxy - no Session
How to resolve my problems?
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId")
private User user;
it really worked. I just tried that on Bi-Directional #ManyToOne mapping. It fixed
com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)
Try to add #JsonIgnore annotation to one of fields to avoid looping
For bidirectional relationships you can use these annotations:
#JsonManagedReference for the parent and #JsonBackReference for the child.
Also, this link might help:
Jackson – Bidirectional Relationships
Snippet must help you.
#JsonIgnore
#ManyToOne
#JoinColumn(name = "columnName", referencedColumnName = "id")
private Class class;

Categories