I have a class User which has a parameter of another class type ShoppingList.
As this...
#Entity
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private Long id;
private String Name;
#?????
private ShoppingList[] shoppingList;
}
How can i make this ManyToOne relationship while the variable being an array?
The idea is to have a User table and another ShoppingList table, so the user can have multiple lists at the same time.
This would be the correct way:
One Employee has many ShoppingLists.
One ShoppingList has only one Employee.
#Entity
public class Employee implements Serializable {
....
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<ShoppingList> shoppingList;
....
}
#Entity
public class ShoppingList implements Serializable {
....
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
....
}
You can fine-tune your entities as per your need.
For more info, I would refer to this tutorial, it has helped me a lot.
Related
Let's say I have those two entities, Person & Insurance. One Person can have multiple insurances, and the insurance uniqueness is maintained by a composite key combination of (insurance type, policy number, and person id). The below code represent the the scenario...
parent class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = "GenerationType.IDENTITY")
#Column(name "person_id")
private Long personId;
#Column(name = "fst_nm")
private String fstName;
#Column(name = "lst_nm")
private String lstNm;
// ..Other columns & relationships
#OneToMany(mappedBy = "person")
private List<Insurance> insurances;
public void addInsurance(Insurance toAdd) {
getInsurances().add(toAdd);
toAdd.setPerson(this);
}
}
child class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Insurance implements Serializable {
#EmbeddedId
private insurancePK id;
//other data
#ManyToOne
#MapsId("personId")
private Person person;
}
composite PK class
#Setter
#Getter
#Embeddable
public class InsurancePK implements Serializable {
#Column(name = "person_id", insertable = false, updatable = false)
private Long personId;
#Column(name = "insurance_type")
private String insuranceType;
#Column(name = "pol_num")
private String polNum;
}
now, my data mapper looks something like that...
Person newPerson = new Person();
newPerson.setInsurances(new ArrayList<>());
// fill out Person Model data
// incoming insurance data
while (incomingData.hasNext()) {
Insurance insuranceData = new Insurance();
InsurancePK pk = new InsurancePK();
// set other insurance data
pk.setInsuranceType("Dental");
pk.setPolNum("123Abc00");
insuranceData.setId(pk);
person.addInsurance(insuranceData);
}
Problem is my person_id inside the composite key is always getting a null value, not sure why (shouldn't the #MapsId takes care of that value)?
I need to fetch that value dynamically, most of the JPA composite key solutions only are setting all the value manually, but that's not my scenario.
return object from saveAndflush()
{
person: {
person_id: 55,
fst_nm: blah,
lst_nm: blah,
insurances: [
{
insurance_pk: {
person_id: null,
insurance_type: "Dental",
pol_num: "123Abc00"
}
//other insurance data
}
]
}
}
any suggestions on what am I missing? Thank you in advance!
Remove the #Column(name = "person_id", insertable = false, updatable = false) annotation from the InsurancePK.personId.
Add the following annotation:
#JoinColumn(name = "name = "person_id"")
to the Insurance.person.
As mentioned in the comments, adding a cascade to my entity column started me on the right track.
just in case, that's the model that worked for me after couple of tries
Parent class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = "GenerationType.IDENTITY")
#Column(name "person_id")
private Long personId;
#Column(name = "fst_nm")
private String fstName;
#Column(name = "lst_nm")
private String lstNm;
// ..Other columns & relationships
// cascade added <-- thanks to SternK
#OneToMany(mappedBy = "person", casecade = CascadeType.ALL, orphanRemoval = true)
private List<Insurance> insurances;
public void addInsurance(Insurance toAdd) {
getInsurances().add(toAdd);
toAdd.setPerson(this);
}
}
Child class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Insurance implements Serializable {
#EmbeddedId
private insurancePK id;
//other data
#ManyToOne
#MapsId("personId")
// annotation added here instead of PK class <-- fix
#JoinColumn(name="person_id", nullable = false, insertable = false, updatable = false)
private Person person;
}
PK class
#Setter
#Getter
#Embeddable
public class InsurancePK implements Serializable {
//annotation removed <-- fix thanks to SternK
private Long personId;
#Column(name = "insurance_type")
private String insuranceType;
#Column(name = "pol_num")
private String polNum;
}
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 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? :)
I'm having trouble persists the following entities:
#Entity
#Table(name="entityOne")
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne")
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
In EntityOneDAO:
em.merge(entityOne);
And it is only persisted to EntityOne and not the list of EntityTwo ... How do I persist the list ?
Thanks all
You need to take care of both:
transitive persistence (using Cascade)
synchronizing both end of the bi-directional association.
So EntityOne should Cascade Persist and Merge to EntityTwo:
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne", cascade = { CascadeType.PERSIST, CascadeType.MERGE})
private List<EntityTwo> entities = new ArrayList<>();
As you can see, you should always initialize your collection classes to avoid unnecessary null checks.
And it's always better to add the following helper child adding utility in your parent classes (e.g. EntityOne)
public void addChild(EntityTwo child) {
if(child != null) {
entities.add(child);
child.setEntityOne(this);
}
}
Then you can simply call:
EntityOne entityOne = new EntityOne();
entityOne.setProperty("Some Value");
EntityTwo entityTwo_1 = new EntityTwo();
entityTwo_1.setName("Something");
EntityTwo entityTwo_2 = new EntityTwo();
entityTwo_2.setName("Something");
entityOne.addChild(entityTwo_1);
entityOne.addChild(entityTwo_2);
entityManager.persist(entityOne);
P.S.
Please remove the #Inject annotation from the EntityTwo class. Entities are not Components.
And persist is much more efficient than merge, when you want to insert new entities.
You should explicitly set each entityTwo objects' entityOne field.
Such that:
entityTwo_1.setEntityOne(entityOne);
entityTwo_2.setEntityOne(entityOne);
entityOne.entities.add(entityTwo_1);
entityOne.entities.add(entityTwo_2);
em.merge(entityOne);
Try this:
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne",
cascade = { CascadeType.ALL})
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
You can read here, about the CascadeType.
edited.