Spring JPA stop relationships creating new tables - java

I am trying to create some relationships and when I have done it is creating new tables, how can I stop this happening.
Here is my parent class.
#Entity
public class Seasons {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int Season_Id;
private String season;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "season")
private List<Fixture> fixtures;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "season")
private List<Referee> referee;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "season")
private List<Team> team;
}
And here is an example of one of the 3 classes I am trying to create a relationship between. This one is the fixture class.
#Entity
public class Fixture {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int fixture_Id;
#OneToOne
private FixtureStats stats;
#OneToOne
private Referee referee;
#OneToOne
private Team Home;
#OneToOne
private Team Away;
private int week;
#ManyToOne
private Seasons season;
}
When I run the program these are the tables that are created:
seasons
seasons.referees
seasons.fixtures
seasons.teams
I would rather not have to have the #ManyToOne private Season season; in the child classes but if it is needed I'll have to live with it. I have tried searching for this but can't find anything on the topic.
Update:
I have now used #JoinColumn, however this only provides a column for fixtures for some reason?
Here is the updated code
#Entity
public class Seasons {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int Season_Id;
private String season;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn
private List<Fixture> fixtures;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn
private List<Referee> referee;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn
private List<Team> team;
Cheers

Related

ManyToOne not persisted

#OneToMany side of the relation populates well but the #ManyToOne side overrides each time(only the last item persists)
#Entity
#Table(name="order")
public class Order {
#Id
#Column(name ="orderId")
private String orderId;
#OneToMany(targetEntity = Items.class,
fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "orderId")
#NotNull
private Set<Items> items;
.......
}
#Entity
public class Items {
#Id
private String itemId;
#ManyToOne
#JoinColumn(name="orderId",referencedColumnName = "orderId")
private Order order;
............
}
#Entity
#Table(name="order")
public class Order {
#Id
#Column(name ="orderId")
private String orderId;
#OneToMany(targetEntity = Items.class,
fetch = FetchType.EAGER, cascade = CascadeType.ALL
,mappedBy = "item_id")
#NotNull
private Set<Items> items;
.......
}
#Entity
public class Items {
#Id
private String itemId;
#ManyToOne
#JoinColumn(name="orderId",referencedColumnName = "orderId")
private Order order;
............
Replace targetEntity = Items.class by mappedBy = "order"
and remove referencedColumnName = "orderId" and #JoinColumn(name = "orderId") from OneToMany.
Also if you really need eager fetching delete it from OneToMany side - ManyToOne is eager by default.

How to build one to many relationship with different entities which have a composite key with Hibernate, JPA?

I want to build entity classes for the following relationship. I want an entity ProductWiseCustomer which has a composite key. Those key also mapped with Product and Customer entities. How to achieve the purpose?
So far what I have done.
Product.java
#Entity
#Table(name = "product")
public class Product {
#Id
private Long productId;
private String productName;
private Decimal productPrice;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = CustomerProductCompound.class)
private Set<CustomerProductCompound> customerProductCompound;
//Constructor
//Setter-getter
}
Customer.java
#Entity
#Table(name = "customerinfo")
public class CustomerInfo {
#Id
private Long customerId;
private String customerName;
private Boolean isActive;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, targetEntity = CustomerProductCompound.class)
private Set<CustomerProductCompound> customerProductCompound;
//Constructor
//Setter-getter
}
CustomerProductCompound.java
#Embeddable
public class CustomerProductCompound
{
#ManyToOne
#JoinColumn(name = "customerId")
private CustomerInfo customerInfo;
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
//Constructor
//Setter-getter
}
While running the application getting the following error:
Use of #OneToMany or #ManyToMany targeting an unmapped class: com.auth.model.CustomerInfo.customerProductCompound[com.auth.model.CustomerProductCompound].
One solution is to use a composite identifier with #EmbeddableId.
#Entity
public class ProductWiseCustomer {
#EmbeddedId
private ProductCustomerKey key;
}
#Embeddable
public class ProductCustomerKey {
#ManyToOne(fetch = FetchType.LAZY)
private Customer customer;
#ManyToOne(fetch = FetchType.LAZY)
private Product product;
}
Please see the hibernate documentation:
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#identifiers-composite-aggregated
CustomerProductCompound as you have defined just the primary key of ProductWiseCustomer. Your collections inside CustomerInfo and Product must contain ProductWiseCustomer items, not its key.
#Entity
#Table(name = "product")
public class Product {
#Id
private Long productId;
private String productName;
private Decimal productPrice;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ProductWiseCustomer> productWiseCustomers;
}
#Entity
#Table(name = "customerinfo")
public class CustomerInfo {
#Id
private Long customerId;
private String customerName;
private Boolean isActive;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ProductWiseCustomer> productWiseCustomers;
}
Notice I added the mappedBy property in the annotations. It needs to point to the property name on the other side that refers to this object. The JPA name, not the SQL name. targetEntity is rarely necessary, and I've suggested orphanRemoval, so that if you remove one from the set, you don't have to manually delete it for it to go away.
As for the ProductWiseCustomer, you do need the same key as shown by Modular Coder
#Embeddable
public class ProductCustomerKey {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "customerId)
private Customer customer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "productId")
private Product product;
}
But I recommend you use #IdClass instead of #EmbeddedId
#Entity
#IdClass(ProductCustomerKey.class)
public class ProductWiseCustomer {
#ManyToOne(fetch = FetchType.LAZY) // should be lazy here
#JoinColumn(name = "customerId)
private Customer customer;
#ManyToOne(fetch = FetchType.LAZY) // should be lazy here
#JoinColumn(name = "productId")
private Product product;
private OffsetDateTime createDate;
private String remarks;
// getters, setters
}

Entities cannot be mapped properly via hibernate annotations

I have an Invoice entity which contains another entity - Counterparty and a list of another entities - Items. While setting up relations between list of items and invoice, there were no problems. However, when I try to set up a similar relation between invoice entity and counterparty entity, I get an error:
#OneToOne or #ManyToOne on pl.coderstrust.model.Invoice.counterparty references an unknown entity: pl.coderstrust.model.counterparty.Counterparty
This is my invoice, which expects to contain only one counterparty and a list of items.
#Entity
#Table(name = "invoices")
public class Invoice implements Comparable<Invoice>, Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private int id;
#Column(name = "date")
private LocalDate date = LocalDate.now();
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name = "nip")
private Counterparty counterparty;
#OneToMany(mappedBy = "invoice", cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JsonBackReference
private List<InvoiceItem> invoiceItems = new ArrayList<>();
This is my item entity, which can be related to one invoice:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private int id;
private String description;
private int numberOfItems;
private BigDecimal amount;
private BigDecimal vatAmount;
#JoinColumn(name = "vat_code")
#Enumerated(EnumType.ORDINAL)
private Vat vat;
#OneToOne(mappedBy = "invoice_id", fetch = FetchType.EAGER, cascade = {CascadeType.DETACH,
CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JsonManagedReference
private Invoice invoice;
This is my counterparty, which is supposed to be related to many invoices:
#Entity
#Table(name = "counterparties")
public class Counterparty implements Serializable {
#Id
#Column(name = "nip")
private String nip;
private String companyName;
private String phoneNumber;
private String bankName;
private String bankNumber;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "counterparty", cascade = CascadeType.ALL)
private Address address;
#OneToMany(mappedBy = "counterparty", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
private List<Invoice> invoices;
What is wrong with invoice-counterparty relations?
That Hibernate error is usually thrown when the class is not added to the hibernate configuration. Hibernate needs to be told all of the classes that serve as entities before it can use them.
How do you make your classes known to Hibernate? I.e. either by adding the class to the Configuration object:
configuration.addClass(Counterparty.class);
or by adding the class into a package that is scanned for entities when you are using Spring?
On another note: there seems to be something odd with the Item class perhaps? It specifies a OneToOne relation to Invoice; should this not be a ManyToOne (meaning that a single invoice can have 0 or more Items)?

Get primary key instead the whole object in OneToMany relationship

I have the following classes:
#Entity
#Table(name = "elements")
#Inheritance(strategy=InheritanceType.JOINED)
#XmlRootElement
public abstract class Elements implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idelement")
private Integer idElement;
#Basic(optional = false)
#Column(name = "code")
private String code;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Alarms> alarmsCollection;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<ElementsHistorical> elementsHistoricalCollection;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "elementsCollection")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<ElementsGroups> elementsGroupsCollection;
//Constructors, getters and setters
}
#Entity
#Table(name = "alarms")
#XmlRootElement(name = "Alarms")
public class Alarms implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idalarm")
private Integer idAlarm;
#JoinColumn(name = "idelement", referencedColumnName = "idelement")
#ManyToOne(optional = false)
private Elements idElement;
//Constructors, getters and setters
}
I created a jersey webservice and a DAO class with CRUD operations to treat Alarms data. With this implementation, when I call GET method, I get the whole Elements object inside the Alarms one. This is the DAO method called:
public List<Alarms> getAlarms(){
Session session = SessionUtil.getSession();
Query query = session.createQuery("from Alarms");
List<Alarms> alarms = query.list();
session.close();
return alarms;
}
I don't want this fetch = FetchType.EAGER fetch type, as I just need the PK of Elements, but after some research this is the only way I found to make my service work.
I tried this and this approach, but I've not been able to make it work.
You can use left outer join fetch in you query.
Query query = session.createQuery("from Alarms alarm left outer join fetch alarm.idElement element");
Now you can keep fetch = FetchType.LAZY (which is default).
Try to add #XmlTransient annotation to fields in Elements class that you want to ignore.
like this :
#Entity
#Table(name = "elements")
#Inheritance(strategy=InheritanceType.JOINED)
#XmlRootElement
public abstract class Elements implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idelement")
private Integer idElement;
#Basic(optional = false)
#Column(name = "code")
#XmlTransient
private String code;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<Alarms> alarmsCollection;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<ElementsHistorical> elementsHistoricalCollection;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "elementsCollection")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<ElementsGroups> elementsGroupsCollection;
//Constructors, getters and setters
}
You can use multiselect with Tuple, for select only specific columns.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Alarms> root = cq.from(Alarms.class);
CollectionJoin<Object, Object> joinCollection = root.joinCollection("alarmsCollection");
cq.multiselect(joinCollection.get("idElement"));
List<Tuple> tupleResult = entityManager.createQuery(cq).getResultList();
for (Tuple t : tupleResult) {
Long idElement = (Long) t.get(0);
}

Delete both parent and child in #OneToMany relationship

I have an entity called itineraryTraveller, and every itineraryTraveller can have many flightEntity. When I try to delete an itineraryTraveller (parent), from the database, I get this error message:
a foreign key constraint fails (`pquino01db`.`ITINERARYTRAVELLER_FLIGHTENTITY`, CONSTRAINT `FK_ITINERARYTRAVELLER_FLIGHTENTITY_flights_ID` FOREIGN KEY (`flights_ID`) REFERENCES `FLIGHTENTITY` (`ID`))"
Here is my itineraryTraveller entity:
#Entity
public class itineraryTraveller implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<flightEntity> flights;
#Temporal(javax.persistence.TemporalType.DATE)
private Date departureDate;
private String departureLocation;
private String arrivalLocation;
private double cost;
private char status;
private ArrayList<String> stops;
private String stopPrint;
private String userName;
private int iden;
// ...
}
And the flightEntity looks like this:
#Entity
public class flightEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Temporal(javax.persistence.TemporalType.DATE)
private Date departureDate;
private String airlineCode;
private String flightNumber;
private String departureLocation;
private String arrivalLocation;
private double businessCost;
private double economyCost;
private int numBusinessSeats;
private int numEconomySeats;
// ...
}
Can someone see the problem? I think my #OneToMany annotation might be missing something, but I'm not sure what. I want to delete both the parent and child at the same time.
Your relationship between the two entities is unidirectional as there is no mapping from flightEntity back to itineraryTraveller entity as you do not have a #JoinColumn on your flightEntity. There can be one of the following solutions for your problem:
Add a #ManyToOne annotation on the flightEntity as follows:
#Entity
public class flightEntity implements Serializable {
// ....
#ManyToOne
#JoinColumn(name="<name_of_foreignkey_column>")
private itineraryTraveller traveller;
// ...
}
And you have to add a mappedBy attribute to your #OneToMany annotation:
#OneToMany(mappedBy="traveller", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
Thereby making the relationship between the entities bidirectional.
This one can solve the problem if you already have tables in the database with a foreign key relationship.
Use #JoinTable annotation on the #OneToMany annotation:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name="<join_table_name>", joinColumns=#JoinColumn("TRAVELLER_ID"), inverseJoinColumns=#JoinColumn("FLIGHT_ID"))
private List<flightEntity> flights;
(The names of the columns are considered to be examples, and can be changed.)
This last mapping is useful if you don't have tables in the database with foreign key column defined, and it will create a new table as an association between the tables; which is normally the case in a many-to-many relationships.
If it is possible use #ManyToOne annotation on the flights entity. This is normal way of mapping a one-to-many relationships.
Lastly, there are conventions in Java that state class names should begin with a capital letter. So I would rename the entity names to Flight and ItineraryTraveller.
Note that in some cases the #JoinColumn on the child object must have insertable = false and updatable = false like this:
#JoinColumn(name = "user_id", insertable = false, updatable = false)
public class User {
private List<UserRole> roles;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "user")
public List<UserRole> getRoles() {
return this.roles;
}
public void setRoles(List<UserRole> roles) {
this.roles = roles;
}
}
public class UserRole {
#ManyToOne
#JoinColumn(name = "user_id", insertable = false, updatable = false)
private User user;
}

Categories

Resources