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;
...
Related
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
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.
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.
I'm having problems mapping composite keys in jpa / hibernate. The parent entity and the child entity both have composite primary keys.
I have been able to use #mapsId when the parent entity has a simple key and the child has a composite key.
In the hibernate documentation they use #JoinCoumns in the mapping to demonstrate mapping two composite keys. But in their example its not clear where those column references are defined.
I have the following:
#Embeddable
public class PriceRequestLegKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
...
}
#Embeddable
public class AllocationKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
#Column(name = "allocation_index")
private int allocationIndex;
...
}
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private List<AllocationModel> allocations;
...
}
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
At runtime when saving it gives the follow exception:
org.springframework.orm.jpa.JpaSystemException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex; nested exception is org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex
Which I assume is spurious as there are getters and setters. I also get the same error if I use mappedBy="leg" on the priceRequestLegModel and #MapsId on the AllocationModel. Could anyone point out what I'm doing wrong here?
You should restore the mappedBy="leg" to the PriceRequestLegModel #OneToMany annotation:
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(mappedBy="leg", cascade = CascadeType.ALL)
private List<AllocationModel> allocations;
...
}
Then you should change AllocationKey to reference PriceRequestLegKey:
#Embeddable
public class AllocationKey implements Serializable {
PriceRequestLegKey legKey; // corresponds to PK type of PriceRequestLegModel
#Column(name = "allocation_index")
private int allocationIndex;
...
}
And then set the value of the Allocation.leg #MapsId annotation appropriately:
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId("legKey")
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
Some examples like this are in the JPA 2.2 spec section 2.4.1.
I have two tables which have Many-to-Many relations which have a JoinTable USER_SERVICES as below.
#Entity
public class User implements Serializable {
#NotNull
#Column(unique=true)
private String username;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "USER_SERVICES",
joinColumns = {#JoinColumn(name = "id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "", referencedColumnName = "name")})
private Set<Services> services;
// Getters and Setters
}
#Entity
public class Services implements Serializable {
#NotNull
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long serviceId;
#NotNull
#Column(unique=true)
private String name;
//Getters and Setters
}
The above code creates a table USER_SERVICES, but I also want to have a Many-to-Many relation on the table USER_SERVICES with another table RATINGS which would result in another table USER_SERVICES_RATINGS. how can I define this relation with Hibernate/JPA annotations?
Bi-Directional Many to Many using user managed join table object (Relatively common)
Commonly used when you want to store extra information on the join object such as the date the relationship was created.
public class Foo{
private UUID fooId;
#OneToMany(mappedBy = "bar", cascade=CascadeType.ALL)
private List<FooBar> bars;
}
public class Bar{
private UUID barId;
#OneToMany(mappedBy = "foo", cascade=CascadeType.ALL)
private List<FooBar> foos;
}
#Entity
#Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;
#ManyToOne
#JoinColumn(name = "fooId")
private Foo foo;
#ManyToOne
#JoinColumn(name = "barId")
private Bar bar;
//You can store other objects/fields on this table here.
}
You need to create an explicit UserServices entity and setup the relationship to the Ratings entity per your needs.
Remember that in hibernate you model relationships between entities (i.e. your java objects), not db tables.