I am working on a Spring boot (MVC, JPA) application and it is required to return different attributes on different requests. I found the #JsonView annotation and it seems to work. But do I need to annotate every attribute with a basic view?
Example:
Entity1
#Entity
public class Entity1 implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonView(JsonViews.ExtendedView.class)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "entity1", fetch = FetchType.EAGER)
List<Entity2> entities2;
#JsonView(JsonView.ExtendedView.class)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "entity1", fetch = FetchType.LAZY)
List<Entity3> entities3;
}
Entity2
#Entity
public class Entity2 implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Entity3
#Entity
public class Entity3 implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Views
public class JsonViews {
public static class BasicView { }
public static class ExtendedView extends BasicView { }
}
Controller
#RequestMapping(method = RequestMethod.GET)
#JsonView(JsonViews.BasicView.class)
public #ResponseBody List<Entity1> index() {
return repositoryEntity1.findAll();
}
This is a trimmed example but I think it applies to the problem. I expect that the controller returns the Ids and the list of Entity2 objects. But it returns an empty object with "No Properties". If I annotate every attribute of every class involved in this request, it seems to work, but is this really needed or the best solution? Is there a way to define a "DefaultView"?
thanks
Edit: If I annotate the JpaRepository it returns the entire object including the list with Entity3 objects.
No, you do not need to define views on all properties. Insert
spring.jackson.mapper.default-view-inclusion=true
in your application.properties. This will cause properties without the #JsonView annotation to be included in the response and only the annotated properties will be filtered.
In your Controller, properties without a view or with the BasicView annotated will be returned.
Related
I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository
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());
I designed a database model like this. In this, I'd like to store user info from Facebook when they use Facebook or Google to login.
I use JPA with Hibernate to work with Database, so I need to map the database model to ORM.
What I tried:
I tried MappedSuperclass for SnsProfile abstract class but It doesn't work when I need to map SnsProfile with User entity.
I tried #Inheritance(strategy = InheritanceType.SINGLE_TABLE) but if I have some more Social Sites and additional properties, the table is more and more complicated.
I would like to have some JPA Entity like this:
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String password;
private String email;
private UserStatus status;
#OneToMany(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "user")
private List<SnsProfile> profile;
}
public abstract class SnsProfile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String snsId;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "user_id", nullable = false)
private User user;
}
public abstract class FacebokProfile extends SnsProfile {
// Properties here
}
public abstract class GoogleProfile extends SnsProfile {
// Properties here
}
Could you please give me some advice on Entity design to the database model above work well on JPA? Or any changes in Database model are welcome.
Please help. Thank you so much!
Created a simple spring boot project using SpringBoot 1.5.15.BUILD-SNAPSHOT with data-jpa and spring-mvc(web) with 2 entities.
Parent entity
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "parent_id")
private Collection<Child> children;
//Getter & Setter remove for brevity
}
Child entity
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
}
Whenever an endpoint to fetch all Parent data is called, children data is also returned, yet, from my understanding by default fetchType is LazyLoading.
The following Spring Mvc rest code used to fetch data
#RestController
#RequestMapping("test")
public class Controller {
#Autowired
ParentRepository parentRepository;
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<Collection<Parent>> findAll(HttpServletRequest request) {
List<Parent> parents = parentRepository.findAll();
return new ResponseEntity<>(parents, HttpStatus.OK);
}
}
What is expected and that is what should happen is only Parent data and not with Children data collection, should be fetched as they are lazyloaded.
How can i stop this undesired behavior of always eager loading children.
N.B: I tried setting fetchType to LazyLoad, though still when i call '/test', children data is also fetched
Finally i managed to stop Jackson from kindly pulling lazy-loaded data for me.
To do that just
Add the following maven dependency
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.6</version>
</dependency>
Then subclass ObjectMapper like so and register hibernate module depending on the hibernate version you are using.I'm using Hibernate 5, so i did this
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
registerModule(new Hibernate5Module());
}
}
Finally add a Bean
#Bean
public ObjectMapper objectMapper(){
return new HibernateAwareObjectMapper();
}
With above code in-place, lazy-loaded attribute are not auto-initialized.
1 parent entity may have 0 or multiple lazy child entities
For example, there is a function changing the status column in parent and child entities, while merge(parent), parent entity is updated but child entities are insert new instead of update.
Both the child entities id, data are exactly the same as in database while debugging.
The parent object is put in #SessionAttributes in spring controller, would it be the reason?
Even I merge only the child list, merge(childList), it create new records instead of update also.
#Entity
#Table(name = "member")
public class Member implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "memberParent", cascade = CascadeType.ALL)
public List<Child> ChildList
getter setter......
}
#Entity
#Table(name = "child")
public class Child implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="member_id")
private int mem_id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "member_id", referencedColumnName = "id", insertable = false, updatable = false)
})
public Member memberParent;
getter setter......
}
//Controller
#SessionAttributes({"member"})
public class Appcontroller {
#Transactional
#RequestMapping(value = {"/update-member/{id}"}, method = RequestMethod.GET)
public String viewEditRepresetative(ModelMap model, #PathVariable ind id) {
Member member = memberService.find(id);
model.addAttributes("member", member);
}
#Transactional
#RequestMapping(value = {"/update-member"}, method = RequestMethod.POST)
public String viewEditRepresetative(ModelMap model, HttpServletRequest reques, #Valid #ModelAttribute("member") Member member, BindingResult result,
RedirectAttributes redirectAttributes, SessionStatus status) {
if (!result.hasErrors()) {
memberService.merge(member);
}
}
I can't see any parent child relationship in your snapshot code.
Please amend the code for child class with below code to create the inheritance relationship.
public class Child extends Member implements Serializable{
Extending the Child class to the Parent(Member) will reflect the required changes related to lazy loading.