Description
I have a system where I can add Component and save it to DB(Mysql). After that a can create Product which contains Components in different amount.
As I understand table should be like component_id|product_id|amount_of_component
Component class:
#Entity
#Table(name = "component")
public class Component {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
}
Product class:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
//something should be here
private Component component;
}
Question
Which annotation or Collection I should use to create such relations between that entities ?
You must use OneToMany with either Set or List. Here is an example:
#OneToMany
#JoinColumn("product_id")
private Set<Component> component;
Please read the Hibernate documentation about Collection mapping: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#collections
Related
Let's say I have the following entities in my application:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
}
Question 1:
I want to load a SomeEntity with id = 1 for example, but I only want to load the anotherEntities partially, for example I only want the last 10 versions of it, what is the easiest and most straight forward way of doing it (with Hibernate/Spring Data JPA) with one request?
Question 2:
I want to update the previously mentioned object and add a new AnotherEntity to the list, but the save(T t) method of JpaRepository saves the whole object and I lose the ones that weren't loaded. How can I save the object so that the version will be updated by Spring Data (Optimistic Locking) and the SomeEntity won't lose previous data?
Update 1:
I am using Postgresql for database.
You have different options depending on your exact constraints.
You can use the #Where annotation:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Where(clause = "version < 10")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
You can use a filter:
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany
#Filter(
name="latestVersions",
condition="version < :version"
)
private List<AnotherEntity> anotherEntities = new ArrayList<>();
}
You can enable filters before running a query with the session:
entityManager
.unwrap(Session.class)
.enableFilter("latestVersions")
.setParameter("version", 10);
List<Account> accounts = entityManager.createQuery(
"from SomeEntity se where se.id = 1", SomeEntity.class)
.getResultList();
You can map the association as bidirectional (or as a many-to-one)
#Data
#Entity
public class SomeEntity {
#Id
private Long id;
#OneToMany(mappedBy = "someEntity")
private List<AnotherEntity> anotherEntities = new ArrayList<>();
#Version
private Long version;
}
#Data
#Entity
public class AnotherEntity {
#Id
private Long id;
#Column
private String someField;
#Column
private Long version;
#ManyToOne
private SomeEntity someEntity;
}
Now you can get the list of entities using an HQL query:
from AnotherEntity ae where ae.someEntity.id = 1 and ae.version < 10
When you want to create a new AnotherEntity, you can get SomeEntity from any element in the result list, or you can use EntityManager#getReference (and avoid running the query):
AnotherEntity ae = new AnotherEntity(...);
ae.setSomeEntity(em.getReference(SomeEntity.class, 1));
em.persist(ae);
The association is lazy, so Hibernate is not going to load the whole collection (unless you need to access it).
I am trying to save a JPA entity which has ManytoMany Relationship (Consumer and Product table) and OnetoOne relation with ConsumerDetailstable.Below are my entities
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity
public class Consumer {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
#JsonManagedReference
#OnToMany(mappedBy = "consumer")
private Set<ConsumerProduct> consumerProducts;
#OneToOne
private CustomerDetails consumerDetails;
}
#Entity
public class Product {
#Id
#GeneratedValue
private Long productId;
private String productCode;
#OneToMany(mappedBy = "product")
private Set<ConsumerProduct> consumerProducts;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity(the join table)
public class ConsumerProduct {
#EmbeddedId
ConsumerProductKey id;
#JsonBackReference
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Consumer consumer;
#ManyToOne
#MapsId("productId")
#JoinColumn(name = "product_id")
private Product product;
}
#Embeddable (forgein keys combined as embeded id)
public class ConsumerProductKey implements Serializable {
#Column(name="id")
private Long id;
#Column(name = "product_id")
private Long productId;
}
#Enitity (one to one relation table)
public class CustomerDetails {
#Id
#GeneratedValue
private Long consumerDtlId;
#OneToOne
private Consumer consumer;
private String city;
private String state;
private String country;
}
To save the entity am have just extended JPARepository and called save method
public class ConsumerRepository<Consumer> Implements JPARepository<Consumer, Long> {
#Override
public Consumer S save(Consumer entity) {
return save(entity);
};
}
I get java.lang.StackOverFlowError at save method.
Anything wrong with my Mappings ?
Question: Since this will be save operation and since Consumer Id is yet to be generated how do I assign to below Entities
ConsumerProduct.ConsumerProductKey (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
CustomerDetails (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
EDIT: I have updated the entity with JsonManagedReference and JsonBackedReference but still i have am facing stackoverflow error
It is due to Consumer trying to access ConsumerProduct and ConsumerProduct trying to access consumer entity and end up with StackOverflow error.
You should use #JsonManagedReference and #JsonBackReference annotation in consumer and ConsumerProduct respectivly.
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 two entity classes, named Product and ComboProduct. Product contains all the information about a particular product and has a Primary Key productId. ComboProduct holds a list with a #OneToMany relationship to the Product and has a primary key named comboProductId. When I create multiple Comboproduct instances with the same set of products, I get the following DataIntegrityViolationException:
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#OneToOne
private MasterProduct masterProduct;
#NotBlank
private String productName;
private String productDescription;
private Double productSellingPrice; // mrp
private Double productOfferPrice; // price at which the user sells
private Double productPurchasePrice;
private float totalTaxPercentage;
private float productMargin;
#OneToOne
private MerchantStore store;
#ManyToOne
private ComboProduct comboProduct; #Entity
public class ComboProduct {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long comboProductId;
private String comboProductKey;
#OneToMany
private List<Product> products;
#NotBlank
private String comboName;
private String comboDescription;
org.springframework.dao.DataIntegrityViolationException:
could not execute statement; SQL [n/a];
constraint [UK_nm4dyaqp2f780nx73vq9abbw3];
nested exception is org.hibernate.exception.ConstraintViolationException:
could not execute statement\n\t
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:298)\n\t
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)\n\t
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:538)\n\t
at org.springframework.transaction.support.
How can I resolve this?
as you have modeled a One-to-Many relationship, you defined that one combo is associated to multiple products. But products can only be assigned to one combo.
If you want products to be able to belong to multiple combos, you would need to model a Many-to-Many relationship. That would of course also imply to have a respective mapping table on the database.
Relationships between the 2 entity classes were not established properly.
`
#Entity
public class Product {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Long productId;
#ManyToOne
private ComboProduct comboProduct; }
#Entity
public class ComboProduct {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long comboProductId;
#OneToMany
private List<Product> products;
} `
The problem is you need to tell Hibernate that the OneToMany mapping will use a foreign key on another table, otherwise, it will assume that the entity in the list must have a unique Id.
To do so, you must add #JoinColumn on the attribute owning the mapping (class ComboProduct attribute products).
Try the following:
#Entity
public class ComboProduct {
...
#OneToMany(mappedBy = "comboProduct")
private List<Product> products;
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productDescription;
private Double productSellingPrice;
private Double productOfferPrice;
#ManyToOne
#JoinColumn
private ComboProduct comboProduct;
}
I want to use mixed #Inheritance strategy, but Hibernate doesn't support it.
Is there any way to implement JOINED inheritance without actual class inheritance.
For example:
#Entity
#Table(name="A")
#Inheritance(strategy=InheritanceType.JOINED)
public class A {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_SEQ")
private Long id;
//getters
//setters
}
#Entity
#Table(name="B")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class B {
#Id
private Long id;
//getters
//setters
}
So, basically in B I just want to refer to #Id generated in A without extending from A.
I found the solution. JPA doesn't allow you to combine #Id and #OneToOne. However, #MapsId annotation does the trick:
#Entity
public class A {
#Id
private Long id;
//getters
//setters
}
#Entity
public class B {
#Id
private Long id;
#MapsId
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(nullable=false, name="id")
private A a;
//getters
//setters
}
I think you can accomplish this by making a #OneToOne relationship or a #OneToMany and point the table name like this
#Id #OneToOne
#JoinColumn(name = "id")
private A a;