JPA ManyToOne/OneToMany bidirectional get duplicate data - java

I am building a REST API with Spring 2.1 and I am getting duplicate data to consult the ManyToOne relationship.
Localidad:
#Entity
#Table(name = "localidad")
public class Localidad implements Serializable {
private static final long serialVersionUID = -7258462202419598287L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idLocalidad;
private String nombreLocalidad;
private BigDecimal precioEnvio;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "provinciaIdProvincia")
private Provincia provincia;
public Localidad() {}
public Localidad(String nombreLocalidad, BigDecimal precioEnvio) {
this.nombreLocalidad = nombreLocalidad;
this.precioEnvio = precioEnvio;
}
...
Provincia:
#Entity
#Table(name = "provincia")
public class Provincia implements Serializable {
private static final long serialVersionUID = 3324427184301992595L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idProvincia;
private String nombreProvincia;
#OneToMany(mappedBy= "provincia", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Localidad> localidades = new HashSet<Localidad>();
public Provincia() {}
public Provincia(String nombreProvincia) {
this.nombreProvincia = nombreProvincia;
}
...
I access information by implementing CrudRepository and Service #Autowired
Duplicate data HTTP GET Request:
Duplicate Data
Thanks

The problem is caused by Jackson doing cyclic serialization on the provincia and the localidades fields. This can be solved by using the #JsonIgnoreProperties annotation. so in the Localidad class or entity add the annotation as follows:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "provinciaIdProvincia")
#JsonIgnoreProperties("localidades")
private Provincia provincia;
And in the Provincia class modifiy the Set<Localidad>(btw you can just use a List<Localidad> instead) like this:
#OneToMany(mappedBy= "provincia", cascade = CascadeType.ALL, fetch =
FetchType.LAZY)
#JsonIgnoreProperties("provincia")
private Set<Localidad> localidades = new HashSet<Localidad>();
With this changes your Rest API should now show no duplicates. You have to ignore the fields that define the association between the two classes or entities. In case you have used #JsonProperty to define the fields names, use the names used in the #JsonProperty definition for #JsonIgnoreProperties.

Related

How to audit a #JoinTable with #ManyToMany

I'm working on a Spring-Boot project with a H2 database. I have two entities Portfolio and Report, and there is a many-to-many association between the two.
I want those entities to be audited, so I followed this tutorial to audit through an AuditorAware interface with custom fields.
The two entities are well audited, the columns are created in the database. However, the join table portfolio_reports is not audited. How can I audit the join table as well ?
Portfolio.java
#Entity
#Table(name = "portfolio")
public class Portfolio extends Auditable<String> implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
#Unique
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "portfolio_report", joinColumns = #JoinColumn(name = "portfolio_id"), inverseJoinColumns = #JoinColumn(name = "report_id"))
private List<Report> reports;
// Getters and setters
}
Report.java
#Entity
#Table(name = "report")
public class Report extends Auditable<String> implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "axioma_id")
private Long axiomaId;
#Column(name = "name")
private String name;
#AuditJoinTable
#ManyToMany(mappedBy = "reports", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
private List<Portfolio> portfolios;
// Getters and setters
}
Auditable.java
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#Version
#Column(name = "version_no")
protected Long versionNo;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_date")
protected Date createdDate;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "modified_date")
protected Date modifiedDate;
}
AuditorAwareImpl.java
public class AuditorAwareImpl implements AuditorAware<String> {
#Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Admin");
}
}
PersistenceConfiguration.java
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class PersistenceConfiguration {
#Bean
public AuditorAware<String> auditorAware() {
return new AuditorAwareImpl();
}
}
Problem:
Clearly here Auditable should add some column to your intermediate table that maintains relation between Portfolio and Report and that table is created behind the scene and you don't have access to that table in your program. Only hibernate can use that table to maintain relation between your entities and do join operation.
Solution:
Here you should make Join table that maintain Many to Many relation between Portfolio and Report explicit so that you can have entity like PortfolioReport in your program that can extends from Auditable. Please read the following post to see how to do that: The best way to map a many-to-many association with extra columns when using JPA and Hibernate

Persisting object does not persist all its relational objects

I am have three entities with relations look like this:
Customer and Order: one-to-many
Order and Article: one-to-many
Here are my Java Classes:
#Entity
#Table
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
#OneToMany(mappedBy = "order")
private List<Article> orderedArticles;
}
#Entity
#Table
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "customer_id")
private long id;
#OneToMany(mappedBy = "customer")
private List<Order> orders;
}
#Entity
#Table
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "article_id")
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
And then I persist the order to the database:
Customer customer = createCustomer()
List<Article> articles = createArticles();
Order order = new Order(customer, articles)
entityManager.persist(order);
The order and customer were successfully persisted but strangely that the article not. Can anyone help me here what did i do wrong? How can I make one call to persist the order and parallel the customer and article will be also persisted?
Thank you very much!
Try change
#OneToMany(mappedBy = "order")
private List<Article> orderedArticles;
to
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;
As it mentioned in this article:
Cascading only makes sense for Parent – Child associations (the Parent entity state transition being cascaded to its Child entities). Cascading from Child to Parent is not very useful and usually, it’s a mapping code smell.
So, for example your Order - Article association should be corrected in this way:
#Entity
#Table
public class Order implements Serializable {
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;
}
#Entity
#Table
public class Article implements Serializable {
#ManyToOne // CascadeType.ALL should not be used here
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
The same correction should be made for the Customer - Order association.
You use bidirectional #OneToMany. So, as documentation states:
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
For example your Order entity should have the following methods:
#Entity
#Table
public class Order implements Serializable {
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;
public void addArticle(Article article) {
orderedArticles.add(article);
article.setOrder(this);
}
public void removeArticle(Article article) {
orderedArticles.remove(article);
article.setOrder(null);
}
}
to make the bidirectional association Order - Article in-sync. The same correction should be made for the Customer - Order association.
Assuming that your Order and Customer entities have the appropriate helper methods, the valid example of persisting can look like this:
Article article1 = new Article();
// ...
Article article2 = new Article();
// ...
Order order = new Order();
order.addArticle(article1);
order.addArticle(article2);
Customer customer = new Customer();
customer.addOrder(order);
entityManager.persist(customer);
So, you should start from articles creation, then add them to the order (or orders), then add your order (or orders) to the customer entity and then persist the customer. Due to the usage of CascadeType.ALL all children entities will be persisted too.

Hibernate - the custom query did not find the entity by the child parameter for ManyToOne unidirectional relation

I have a problem with retrieving an entity using the child's entity as a search parameter. Entities are related to many to one relationship as unidirectional and each object is fetched as FetchType.LAZY.
When I looking for an entity by a child entity, the result is null. But when I set to fetch as Eager it is correct.
My Entities:
#NoArgsConstructor
#Getter
#Entity
#Table(name = "partner")
public class PartnerEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String login;
public PartnerEntity(String login) {
this.login = login;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "point")
public class PointEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
public PointEntity(PartnerEntity partnerEntity) {
this.partnerEntity = partnerEntity;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "orer")
public class OrdEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PAYMENT_POINT_ID")
private PointEntity pointEntity;
public OrdEntity(PointEntity pointEntity) {
this.pointEntity = pointEntity;
}
}
#NoArgsConstructor
#ToString
#Getter
#Entity
#Table(name = "BL")
public class BLEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARTNER_LOGIN", referencedColumnName = "login")
private PartnerEntity partnerEntity;
private String number;
public BLEntity(PartnerEntity partnerEntity, String number) {
this.partnerEntity = partnerEntity;
this.number = number;
}
}
And I looking for BLEntity using OrdEntity child:
final OrdEntity byId = ordRepo.findById(id);
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
final BLEntity blEntityResult= blRepo.findOneByNumberAndPartner(number, partnerEntity);
The object partnerEntity is not null, it is correct object.
I got blEntityResult as null but if I change in PointEntity fetch to FetchType.EAGER, blEntityResult is not null(correct).
My custom query in repository below:
public interface BLRepo extends JpaRepository<BLEntity, Long> {
#Query("select b from BLEntity b where b.number = :number and b.partnerEntity= :partner")
BLEntity findOneByNumberAndPartner(#Param("number") String number, #Param("partner") PartnerEntity partner);
}
why does happens, if the partner object being downloaded is not null and is correct?
I think you should add the mapping in both sides,
because of default fetch type for #AllToMany=Lazy and #ManyToAll = Eager.
just add below code inside PartnerEntity.
#OneToMany(mappedBy="partnerEntity" , fetch = FetchType.Eager )
List<BLEntity> blEntity = new ArrayList<>();
I change FetchType into Eager in PointEntity:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
And everything is ok, but I don't understand why it does not work with PaymentType.Lazy. When I am looking for:
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
I get correct entity "PartnerEntity" which has proper login's field (login'field has value "test").
When I turned logged level to 'TRACE' I saw, Hibernate not binding correct login's parameter, it set null instead "test") why? :)

How to get data from joined table with where clause in JPA?

i have an Entity with following mappings:
#Entity
#Table(name = "template_product")
public class TemplateProductBean implements Serializable {
private static final long serialVersionUID = -1821696115330320798L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "product_id")
private Long productId;
// bi-directional many-to-one association to
// Template
#ManyToOne
#JoinColumn(name = "template_id")
private TemplateBean template;
The Template Bean looks as following:
#Entity
#Table(name = "template")
public class TemplateBean implements Serializable {
private static final long serialVersionUID = 3752018564161042623L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "platform_id")
private Long platformId;
#Column(name = "template_name")
private String templateName;
// bi-directional many-to-one association to
// TemplateProduct
#OneToMany(mappedBy = "template")
private List<TemplateProductBean > templateProductBean;
The Problem im facing is, im very untrained when it comes down to use the JPA Interfaces. How can i use these to get the following query:
select template
from Template template
join TemplateProductBean templateProducts
on template.templateId = templateProducts.template.templateId
where templateProducts.productId in :templateProductIdList
How can i use the JPA Interfaces( javax.persistence.criteria.Root, javax.persistence.Join etc.) to build this query as a Predicate? I'm sorry if im being unclear, i have to use this and im not used to using the JPA. Thanks
I think this should work
select t from TemplateBean t inner join t.templateProductBean tpb where tpb.productId in :templateProductIdList
More details on inner joins and HQL in general: documentation

org.hibernate.PropertyAccessException while persisting ManyToMany relationship

Have a problem persisting a ManyToMany relationship mapped like that
Document.java
public class Document {
.......
#ManyToMany(targetEntity = Category.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "fideuram_gup_documents_in_categories",
joinColumns = #JoinColumn(name="fk_document"),
inverseJoinColumns = #JoinColumn(name = "fk_category"))
private Set<Category> categories = new HashSet<Category>();
.......
}
where Category is one more entity of my model which I don't paste here since it doesn't carry a reverse mapping of this relation, and has just an ID and a name.
When I try to persist Document however I get the following error:
org.hibernate.PropertyAccessException: could not get a field value by reflection getter of it.ardesia.fideuram.gup.model.Category.id
I've surfed the web about it but no page relates to ManyToMany relations. Of course all the ManyToOne relations I have on the entity Document work fine.
I'm using:
spring-data-jpa:1.2.0.RELEASE
hibernate-core:4.2.2.Final
hibernate-entitymanager:4.2.2.final
UPDATE
All entities expose a default constructor and getter/setter for every field. Or,more preciselt, I'm using Spring Roo for creating the entity and it injects getters and setters automatically upon compilation.
You can instrument Hibernate how it must access your property using #javax.persistence.Access annotation; put on your mapped class with #Access.value set to
AccessType.FIELD for direct field access
AccessType.PROPERTY for accessing properties using accessors
Maybe it can help you, I already did the same, I put my code, it creates a join table:
#Entity
#Table(name = "custom_pizza")
public class CustomPizza extends BaseEntity {
private static final long serialVersionUID = 1L;
// ManyToMany instead of oneToMany in order to don't have the unique
// constraint on each primary key of the join table
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "custom_pizza_topping", joinColumns = #JoinColumn(name = "custom_pizza_id"), inverseJoinColumns = #JoinColumn(name = "topping_id"))
private Set<Topping> toppings = new HashSet<Topping>();
public void addTopping(Topping topping) {
toppings.add(topping);
}
public void removeTopping(Topping topping) {
toppings.remove(topping);
}
...
And my topping:
#Entity
#Table(name = "topping")
public class Topping extends BaseEntity {
private static final long serialVersionUID = 1L;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "price", nullable = false)
private float price;
....
and the BaseEntity
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
...

Categories

Resources