I am tryng to do a workaround for this issue, that I have entered:
https://github.com/jhipster/generator-jhipster/issues/9639
I did some work (I added findAllWithEagerRelationships in the repository), and the GET method works fine: I get all the master and children.
What is not working, and I need your help, is the POST method:
when I post a parent with some children (pets), the children are not posted with the parent, so children are lost.
so summarizing this is the get result, correct:
[
{
"id": 1002,
"name": "Piera",
"pets": [
{
"id": 1051,
"name": "fido",
"species": "barboncino",
"owner": {
"id": 1002,
"name": "Piera"
}
}
]
}
]
but the post does not work correctly:
{
"name": "newName",
"pets": [
{
"id": 1051
}
]
}
newName is created, but pet 1051 is not attached to it
I am working on a app generated with Jhipster:
entity Owner {
name String required
}
entity Pet {
name String required,
species String required
}
relationship OneToMany {
Owner{pet} to Pet{owner}
}
for the get, I added those two methods that I copied form a manytomany relationship, and they worked:
#Query(value = "select distinct owner from Owner owner left join fetch owner.pets")
List<Owner> findAllWithEagerRelationships();
#Query("select owner from Owner owner left join fetch owner.pets where owner.id =:id")
Optional<Owner> findOneWithEagerRelationships(#Param("id") Long id);
Public class Owner implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "name", nullable = false)
private String name;
#OneToMany(mappedBy = "owner")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Pet> pets = new HashSet<>();
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
public class Pet implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "name", nullable = false)
private String name;
#NotNull
#Column(name = "species", nullable = false)
private String species;
#ManyToOne
#JsonIgnoreProperties("pets")
private Owner owner;
// jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
I expect that when I do a post on a parent with some children, the children (pets) are also posted.
In your Owner service interface and implementation layer (it should be under com.yourpackage.service and com.yourpackage.service.impl) create a new method like
public Optional<Owner> findOneWithChildren(long id) {
Optional<Owner> owner = ownerRepository.findById(id);
owner.ifPresent(o -> {
o.getPets().addAll(petRepository.getByParentId(id);
}
return owner.map(ownerMapper::toDto);
}
And in your Pet repository, create a new method, like
List<Pet> getByParentId(long id);
in the bug report, I could find a workaround without touching the JPA. I still don't know how we can put a new owner associating to it some pets with JPA as I did receive a resolving answer, anyway my workaround works. thanks :)
Related
I have in a repository interface the following query :
#Query("SELECT DISTINCT prb FROM Problem prb
WHERE EXISTS (SELECT act FROM Action act WHERE act.problem = prb AND act.dismissed IS FALSE"))
It is supposed to retrieve the list of problems that have at least one action not dismissed.
It does return a list of problems, the list size is correct, but all the fields values are null as you can see below (so I can't judge if the items are what have to be returned):
[
{
"prbId": null,
"description": null,
"domaine": null,
"who": null,
"what": null,
"when": null,
"where": null,
"action": null,
"zone": null
}
]
I am using PostgresSQL, and here are my Entities:
#Entity
public class Problem implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "prb_id")
private long prbId;
private String description;
private String domaine;
private String who;
private String what;
private String where;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm", shape = JsonFormat.Shape.STRING)
private LocalDateTime when;
#JsonIgnore
#OneToMany(mappedBy = "problem", cascade = CascadeType.ALL)
private List<PlanAction> plansAction = new ArrayList<>();
// constrctor, getters and setters ...
}
#Entity
#Table(name = "actions")
public class Action implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long actId;
private String description;
#JsonFormat(pattern = "yyyy-MM-dd", shape = JsonFormat.Shape.STRING)
private LocalDate dueDate;
private String status;
private boolean dismissed;
#ManyToOne()
#JoinColumn(name = "problem_id")
private Problem problem;
// constructors, getters anf setters
}
could any explains why this happens, and how to make the list elements appear with their values
not null.
Thanks in advance.
Repost from here
Given entities and repository:
#Entity
public final class Partner {
#Id
private String id;
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
private List<Merchant> merchants;
...
}
#Entity
public final class Merchant {
#Id
private String id;
#Column
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Partner partner;
...
}
public interface PartnerRepository
extends JpaRepository<Partner, String>, QueryDslPredicateExecutor<Partner> {
}
If there is only one partner having two merchants in the DB then the following code incorrectly returns list with two instances of the same parnter.
partnerRepository.findAll(new Sort("merchants.name"));
This is caused internally by the DB join. By creating custom implementation that adds the distinct to the selection the result is correctly the single partner.
Wouldn't it be correct to do distinct selection per default?
Try
#OneToMany(mappedBy = "partner", fetch = FetchType.LAZY)
#OrderBy("name")
private List<Merchant> merchants;
I am using JPA with JSF datatable with lazy loading.
Here One car can be owned by many users. So when i logged in to the application i want the cars which is owned by the user logged in(assume it as userId=1).
I have a mapping table "Cars_User" that contains carId and userId columns.
My Entities are like this
My Car Class
#Entity
#Table(name="car")
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
#Transient
private boolean myCar;
#NotNull
#Size(min = 1, max = 50)
public String name;
#OneToMany(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinTable(name = "Cars_User", joinColumns = #JoinColumn(name = "carId"), inverseJoinColumns = #JoinColumn(name = "userId"))
private List<User> carUsers = new ArrayList<User>();
getters ...
setters ...
}
User Class
#Entity(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstName;
private String lastName;
}
I have found one answer for Lists of String collection in this link but how can be achieved in my case.
I wanted to do get all Cars entities in criteria api that contains the logged in user id "userId" in carUsers Lists. can anyone please help?
I found the solution. I have passed the logged in user Object "user" in isMember function. This may help for somebody.
CriteriaBuilder criteriaBuilder = em.getEntityManagerFactory().getCriteriaBuilder();
CriteriaQuery<Car> criteria = criteriaBuilder.createQuery(Car.class);
Root<Car> root = criteria.from(Car.class);
criteria.where(criteriaBuilder.isMember(user, root.get(Car_.carUsers)));
List<Car> cars = em.createQuery(criteria).getResultList();
i have this Rest-DSL:
// this api creates new user
rest("/user")
.post()
.type(User.class).to("jpa://com.project.User")
This is my entities:
public class User{
#Id
private String id;
#ManyToOne
#JoinColumn(name = "id_role")
private Role role;
}
public class Role{
#Id
private String id;
#OneToMany(mappedBy="user")
private List<User> users;
}
my problem is in my swagger in the Body value parameter example. It contains like this:
{
"id": "string",
"role": {
"id": "string",
"users": [
{
"id": "string",
"roles": [
{}
]
}
]
}
}
quite complicated, although i need only id and id_role parameters to create (POST) new user. I hope the body example shows like this:
{
"id": "string",
"id_role": "string"
}
I realized that my entities are not created properly. These was i learned:
Configure CascadeType in associated JPA entities
#Entity
public class User {
#Id
private String id;
#ManyToOne
#JoinColumn(name = "id_role")
private Role role;
}
#Entity
public class Role{
#Id
private String id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<User> users;
}
to make class not recursive, set #JsonIgnore
#Entity
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class User{
#Id
private String id;
#ManyToOne
#JoinColumn(name = "id_role")
private Role role;
}
#Entity
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Role{
#Id
private String id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
#JsonIgnore
// this attribute will not appear inside Role class
private List<User> users;
}
I've seen other posts about this problem, but have found no answer to my own troubles. I have
#Entity
#Table(name= ServerSpringConstants.COMPANY)
public class Company implements Serializable {
private static final long serialVersionUID = -9104996853272739161L;
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column (name = "companyID")
private long companyID;
#OneToMany (targetEntity = Division.class, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch = FetchType.EAGER)
#JoinTable (name = "companyDivisionJoinTable",
joinColumns = #JoinColumn(name="companyID"),
inverseJoinColumns = #JoinColumn(name="divisionID")
)
private Set<Division> divisions = new HashSet<>();
public long getCompanyID() {
return companyID;
}
public Set<Division> getDivisions() {
return divisions;
}
public void setDivisions(Set<Division> divisions) {
this.divisions = divisions;
}
}
On the other side:
#Entity
#Table(name= ServerSpringConstants.DIVISION)
public class Division implements Serializable {
private static final long serialVersionUID = -3685914604737207530L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
#Column(name = "divisionID")
private long divisionID;
#ManyToOne
(fetch = FetchType.LAZY, optional = false, targetEntity = Company.class,
cascade = {CascadeType.PERSIST, CascadeType.MERGE
}
)
#JoinColumn(name="companyID", referencedColumnName = "companyID")
private Company company;
public long getDivisionID() {
return divisionID;
}
public void setDivisionID(long divisionID) {
this.divisionID = divisionID;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
Yet for some reason, LAZY loading not working. I'm using JPA. I'm calling back the companies, and their enclosing divisions from within a 'User' class -- the pertinent part
#ManyToMany (targetEntity = Company.class,
cascade={
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH},
fetch=FetchType.EAGER )
#JoinTable (
name="companyUserJoinTable",
joinColumns=#JoinColumn(name="userID"),
inverseJoinColumns=#JoinColumn(name="companyID")
)
private Set<Company> company = new HashSet<>();
I've searched out existing threads, and have tried adding various suggestions, but nothing has helped!
Any help appreciated.
Thanks!
Since you are loading the divisions set eagerly (with fetch = FetchType.EAGER) and you have a bidirectional association, divisions will be initialized with the parent reference to company. I can't see any problem with it. Jpa loaded the full object tree because you just told it so. A company contains divisions which contain a back reference to the company that loaded them.
To understand it better, since the reason for lazy loading is to reduce the data loaded from database, the owning company is already loaded in session for the divisions, so why not setting the association too?
The #ManyToOne association on the other side takes effect if you load divisions first.
To be more correct with your mapping add also a #MappedBy attribute to the one part of the association. This does not affect fetching behavior but will prevent double updates to the database issued by both ends of the association.