To my understanding a Unidirectional one-to-many relationship has a join table and a Bidirectional one-to-many relationship hasn't.
My application works when I implemented the Unidirectional relationship but I can't seem to get it to work for the bidirectional relationship.
These are the tables I am creating
CREATE TABLE CUSTOMER (customerNo INTEGER PRIMARY KEY, joinDate DATE, customerName VARCHAR2(20));
CREATE TABLE BOOKING (bookingNo INTEGER PRIMARY KEY, bookedDate DATE, custNo INTEGER, itemNo NUMBER(10), itemName VARCHAR2(20), quantity NUMBER(5), bookingDate DATE, bookingValue NUMBER(8, 2), constraint booking_fk foreign key (custNo) references customer(customerNo));
My first class
public class Booking implementes Serializable{
private Timestamp bookeddate;
private Timestamp bookingdate;
#Id()
#GeneratedValue(generator"MY_SEQ_GEN")
#SequenceGenerator(name="MY_SEQ_GEN", sequenceName="MY_SEQUENCE", allocationSize=1)
#Column(name="bookingNo", nullable=false)
private Long bookingno;
private Double bookingvalue;
#Column(length = 20)
private String itemname;
private Long itemno;
private Long quantity;
private Customer customer;
#ManyToOne
public Customer getCustomer() {
return customer;
}
...
my other class
public class Customer implements Serializable {
#Column(length = 20)
private String customername;
#Id()
#GeneratedValue(generator="THE_SEQ_GEN")
#SequenceGenerator(name="THE_SEQ_GEN", sequenceName="THE_SEQUENCE", allocationSize=1)
#Column(name="customerNo", nullable=false)
private Long customerno;
private Timestamp joindate;
#OneToMany(cascade=(CascadeType.ALL), fetch=FetchType.EAGER, mappedBy = "customer")
private List<Booking> bookings = new ArrayList<Booking>();
public List<Booking> getBookings() {
return bookings;
}
my bean falls over after I run this method
public void addBooking(Long custno, Long tickno, Long quantity) {
Customer cust = (Customer) em.createNamedQuery("findCustomerByPrimaryKey").setParameter("eid", custno).getSingleResult();
Booking b = new Booking();
b.setBookeddate(new Timestamp(System.currentTimeMillis()));
b.setCustomer(cust);
b.setTicket((Ticket) em.createNamedQuery("findTicketByPrimaryKey").setParameter("eid", tickno).getSingleResult(), quantity);
cust.addBooking(b);
//persistBooking(b);
}
And this is my error message.
javax.ejb.EJBException: BEA1-001D72BA69DC9E472B1E: Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-00904: "CUSTOMER_CUSTOMERNO": invalid identifier
Error Code: 904
Call: INSERT INTO BOOKING (bookingNo, ITEMNAME, BOOKINGDATE, BOOKINGVALUE, ITEMNO, QUANTITY, BOOKEDDATE, CUSTOMER_customerNo) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
bind => [71, Metallica, 2011-01-05 22:07:17.788, 200.0, 420, 2, 2011-01-05 22:07:17.788, 1526]
Query: InsertObjectQuery(courseworkone.Booking#201a41)
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:801)
Your customer mapping is using the default column name which is "CUSTOMER_CUSTOMERNO", but you have created your table using "custNo". You need to use the correct column name.
i.e.
#ManyToOne
#JoinColumn(name="custNo")
public Customer getCustomer() {
return customer;
}
Related
Tell me how to correctly add a collection consisting of information objects to the database (a table, for example, a family) and so that these objects refer to one user in the user table. At the moment, an error occurs, tried different save options, the error continues:
Request json:
[
{
"name":"luda",
"surname":"Petrova",
"phone":"2353636",
"bus":"black"
},
{
"name":"dima",
"surname":"Petrov",
"phone":"23536336",
"bus":"red"
},
{
"name":"ivan",
"surname":"Petrov",
"phone":"2353",
"bus":"blue"
}
]
RestController :
#RequestMapping(value = { "fam/{id}" },method = RequestMethod.POST)
public User addyfam (#PathVariable Long id, #RequestBody List<FamReq> fammreq){
User usr = userRepo.findById(id).get();
var arr = new ArrayList<Family>();
for(FamReq f : fammreq ) {
arr.add(new Family( f.getName(),f.getSurname(),f.getPhone(),f.getBus()));
}
usr.setFamily(arr);
userRepo.save(usr);
return usr;
}
Entity 1 :
#Entity
#Table(name="usr")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name;
#Column(name="surname")
private String surname;
#OneToOne(
mappedBy = "user",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
#JsonManagedReference
private Passport passport;
#OneToMany(
mappedBy = "user",
cascade = {CascadeType.ALL},
orphanRemoval = true
)
private List<Family> family = new ArrayList<>();
/**get/set and constr **/
}
Entity 2:
#Entity
#Table(name="family")
public class Family {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String name;
#Column(name="surname")
private String surname;
#Column(name="phone")
private String phone;
#Column(name="bus")
private String bus;
#ManyToOne(
fetch = FetchType.LAZY
)
#MapsId
#NotFound(action = NotFoundAction.IGNORE)
private User user;
/**get/set and constr **/
}
Since the primary key identifier is identical to the foreign key, it is necessary that the family table contains only the secondary key, and the user table contains only its primary key.
UPDATE :
#RequestMapping(value = { "fam/{id}" },method = RequestMethod.POST)
public User addyfam (#PathVariable Long id, #RequestBody List<FamReq>
fammreq){
User us = userRepo.findById(id).get();
var arr = new ArrayList<Family>();
for(FamReq f : fammreq ) {
Family savedFamily = famRepo.save(new Family(f.getName(),f.getSurname(),f.getPhone(),f.getBus()));
arr.add(savedFamily);
Family(f.getName(),f.getSurname(),f.getPhone(),f.getBus()));
}
userRepo.save(arr);
return us;
}
If, in essence, the family indicates this:
#ManyToOne( cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
#JoinColumn(name="user_id", referencedColumnName = "id")
private User user;
Error:
ERROR: NULL in column "user_id" of relationship "family" violates NOT NULL constraint
Details: The error line contains (sdgsgsgsegf, luda, 2353636, Petrova, null, null)
If you indicate this:
#ManyToOne(
fetch = FetchType.LAZY
)
#MapsId
#NotFound(action = NotFoundAction.IGNORE)
private User user;
then
Error:
Error: attempted to assign id from null one-to-one property [com.dbtest.springboot.db_model.Family.user]
UPDATE:
the controller is currently in use :
#RequestMapping(value = { "fam/{id}" },method = RequestMethod.POST)
public User addyfam (#PathVariable Long id, #RequestBody List<FamReq> fammreq){
User usr = userRepo.findById(id).get();
var arr = new ArrayList<Family>();
for(FamReq f : fammreq ) {
arr.add(new Family( f.getName(),f.getSurname(),f.getPhone(),f.getBus()));
}
usr.setFamily(arr);
userRepo.save(usr);
return usr;
}
Repo:
#Repository
public interface UserRepo extends JpaRepository<User, Long >{
User findByname(String name);
void save(ArrayList<Family> fm);
}
full error :
Hibernate: insert into family (bus, name, phone, surname, user_id) values (?, ?, ?, ?, ?)
Hibernate: insert into family (bus, name, phone, surname, user_id) values (?, ?, ?, ?, ?)
Hibernate: insert into family (bus, name, phone, surname, user_id) values (?, ?, ?, ?, ?)
2021-11-14 23:31:06.693 ERROR 13192 --- [nio-9091-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.beans.NotReadablePropertyException: Invalid property 'id' of bean class [java.util.ArrayList]: Could not find field for property during fallback access!] with root cause
org.springframework.beans.NotReadablePropertyException: Invalid property 'id' of bean class [java.util.ArrayList]: Could not find field for property during fallback access!
at org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper.getPropertyValue(DirectFieldAccessFallbackBeanWrapper.java:58) ~[spring-data-commons-2.5.6.jar:2.5.6]
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.getId(JpaMetamodelEntityInformation.java:162) ~[spring-data-jpa-2.5.6.jar:2.5.6]
at org.springframework.data.repository.core.support.AbstractEntityInformation.isNew(AbstractEntityInformation.java:46) ~[spring-data-commons-2.5.6.jar:2.5.6]
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.isNew(JpaMetamodelEntityInformation.java:246) ~[spring-data-jpa-2.5.6.jar:2.5.6]
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:596) ~[spring-data-jpa-2.5.6.jar:2.5.6]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
The problem here is that you try to make a relation by Id but new Family( f.getName(),f.getSurname(),f.getPhone(),f.getBus()) does not assign any Id to your Family.
Create Family Object and store it into a variable.
Save your Family with the help of .save(familyObject) only then your Family is assigned with an Id.
Save your User with a Family attached to it.
Your controller should look like this:
for (FamReq f : fammreq ) {
Family savedFamily = familyRepo.save(new Family(f.getName(),f.getSurname(),f.getPhone(),f.getBus(), us.getId()));
arr.add(savedFamily);
}
us.setFamily(arr);wq21`
userRepo.save(us);
You also should have a FamilyRepository like so:
#Repository
public interface FamilyRepo extends JpaRepository<Family, Long>{
}
NOTE: Do not forget to create injection of this class in your Controller.
NOTE: Never override the .save() method in the Repositories.
Plus you have to insure that your user is found by id otherwise throw an exception that will be caught by Global Controller Advice. Because if you do not your JPA will try to create a relation while referring to a NULL value.
I have three tables
CREATE TABLE "ingredient" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"ingredient" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza" (
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
"pizza" VARCHAR(50) NOT NULL
);
CREATE TABLE "pizza_structure" (
"pizza_id" INT NOT NULL,
"ingredient_id" INT NOT NULL,
"amount" INT NOT NULL
);
how to join them, to get Pizzas structure as a Map
#Entity
#Table(name = "ingredient")
public class Ingredient{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Ingredient() {
}
}
#Entity
#Table(name = "pizza")
public class Pizza {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany ????
private Map<Ingredient, Integer> pizzaStructure;
public Pizza() {
}
public Pizza(String name, Map<Long, Integer> pizzaStructure) {
this.name = name;
this.pizzaStructure = pizzaStructure;
}
}
do I need to create #Embeddable class PizzaStructure, if yes when how to use it?
now I'm getting an error
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class:
how to join them, to get Pizzas structure as a Map
It seems to look like this:
#ElementCollection
#CollectionTable(name = "pizza_structure", joinColumns = {#JoinColumn(name = "pizza_id")})
#Column(name = "amount")
#MapKeyJoinColumn(name = "ingredient_id")
private Map<Ingredient, Integer> pizzaStructure;
do I need to create #Embeddable class PizzaStructure
No.
More info is here: Hibernate User Guide - Maps.
Note that table pizza_structure should have foreign keys to pizza and ingredient tables and also unique constrain of pizza_id and ingredient_id, like this (it's postgresql dialect):
create table pizza_structure
(
pizza_id ... constraint fk_structure_pizza references pizza,
ingredient_id ... constraint fk_structure_ingredient references ingredient,
amount ...,
constraint pizza_structure_pkey primary key (pizza_id, ingredient_id)
);
You have a manyToMany relationship between pizza and ingredient and an additional column in your relationship.
I found a similar question here: JPA 2.0 many-to-many with extra column
(I would comment, but i do not have enough reputation.)
I'm having an issue with JPA. Basically, what I have is an entity with a list of an abstract type and I need each element of the list to be persisted in its corresponding table with a foreign key (to relate to the entity). Here is the code:
#Entity(name = "entity")
public class Entity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private BigInteger id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="entity_id", referencedColumnName="id")
private List<AbstractType> types;
}
Abstract type:
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractType{
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private BigInteger id;
private String someProp;
#Column(name="entity_id")
private BigInteger entityId;
}
Type A:
#Entity(name = "type_a")
#Transactional
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class TypeA extends AbstractType{
private String prop1;
private String prop2;
}
Type B:
#Entity(name = "type_b")
#Transactional
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class TypeB extends AbstractType{
private String prop3;
private String prop4;
}
I'm having a SQL error. The generated query tries to update the table of the abstract type (which shouldnt exist). This is part of the query:
update hibernate_sequences set sequence_next_hi_value = ? where
sequence_next_hi_value = ? and sequence_name = 'abstract_type'
insert into type_a (some_prop, entity_id, prop1, prop2) values (?, ?, ?, ?)
insert into type_b (some_prop, entity_id, prop3, prop4) values (?, ?, ?, ?)
update abstract_type set entity_id=? where id=?
As you can see, it's trying to update a table which doesn't (and shouldnt) exist. 'abstract_type' table.
Thanks in advance.
Of course you can't name a class Entity without causing problems.
You can make this much simpler by minimizing the annotations and let JPA do it's work. A Container class holds a collection of AbstractTypes:
#Entity
public class Container {
#Id #GeneratedValue
private Long id;
#OneToMany(cascade=CascadeType.ALL)
private List<AbstractType> types;
public List<AbstractType> getTypes() { return types; }
public void setTypes(List<AbstractType> types) { this.types = types; }
}
The AbstractType is just that:
#Entity
public abstract class AbstractType {
#Id #GeneratedValue
private Long id;
}
And a couple concrete types to extend and inherit from the abstract superclass:
EDIT: A FK back to the Container class can be added with a ManyToOne association.
#Entity
public class TypeA extends AbstractType {
#ManyToOne
private Container container;
}
And:
#Entity
public class TypeB extends AbstractType {
#ManyToOne
private Container container;
}
When I run this Minimal, Complete, Verifiable Example I get the following output:
Hibernate: create table AbstractType (DTYPE varchar(31) not null, id bigint not null, container_id bigint, primary key (id))
Hibernate: create table Container (id bigint not null, primary key (id))
Hibernate: create table Container_AbstractType (Container_id bigint not null, types_id bigint not null)
Hibernate: insert into Container (id) values (?)
Hibernate: insert into AbstractType (container_id, DTYPE, id) values (?, 'TypeA', ?)
Hibernate: insert into AbstractType (container_id, DTYPE, id) values (?, 'TypeB', ?)
Hibernate: insert into Container_AbstractType (Container_id, types_id) values (?, ?)
Hibernate: insert into Container_AbstractType (Container_id, types_id) values (?, ?)
Hibernate: select container0_.id as id1_1_ from Container container0_
model.Container#40591559
I am trying to create a simple program by using java.util.Map. I have created a Customer entity class with a map of Order classes. Here are my Java classes:
Customer.java
#Entity
public class Customer {
#Id
#GeneratedValue
private Integer id;
#OneToMany(mappedBy = "customer")
#MapKey(name = "orderNumber")
private Map<String, Order> orders;
}
Order.java
#Entity
#Table(name="TB_ORDER")
public class Order {
#Id
#GeneratedValue
private Integer id;
private String orderNumber;
#ManyToOne
private Customer customer;
}
Here is my program that tries to save a customer with some orders and then I am trying to display the saved data:
public class AppTest {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
saveCustomer(session);
session = HibernateUtil.getSessionFactory().getCurrentSession();
showCustomer(session);
HibernateUtil.getSessionFactory().close();
}
private static void showCustomer(Session session) {
session.getTransaction().begin();
List<Customer> list = session.createQuery("from Customer").list();
for (Customer customer : list) {
System.out.println("customer id : "+customer.getId()+ ", customer orders : "+customer.getOrders());
}
session.getTransaction().commit();
}
private static void saveCustomer(Session session) {
session.getTransaction().begin();
Customer customer = new Customer();
Order order = new Order();
order.setCustomer(customer); order.setOrderNumber("100");
Map<String, Order> map = new HashMap();
map.put("100", order);
customer.setOrders(map);
session.save(customer); session.save(order);
session.getTransaction().commit();
}
}
After running the program, Hibernate generated below DDL & DML commands:
Hibernate: create table Customer (id number(10,0) not null, primary key (id))
Hibernate: create table TB_ORDER (id number(10,0) not null, orderNumber varchar2(255 char), customer_id number(10,0), primary key (id))
Hibernate: alter table TB_ORDER add constraint FK_qr7tjivmclv5trf0sbwxki4v foreign key (customer_id) references Customer
Hibernate: insert into Customer (id) values (?)
Hibernate: insert into TB_ORDER (customer_id, orderNumber, id) values (?, ?, ?)
Hibernate: select customer0_.id as id1_0_ from Customer customer0_
customer id : 1, customer orders : null
Hibernate generated only single select query to get the Customer details but not getting any data from TB_ORDER table. So when I try to get the Orders from Customer I am getting null. Please let me know where is the mistake in this code.
It looks like you are missing the #JoinColumn annotation through which you tell who is the owner of the relationship. For this reason i think you should change the class order like this:
#Entity
#Table(name="TB_ORDER")
public class Order {
#Id
#GeneratedValue
private Integer id;
private String orderNumber;
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
}
And should work fine.
You have to also use #MapKeyJoinColumn as well its better to initialize your Map in pojo and try again .
private Map<String, Order> orders =new Map<String, Order>();
Here is my code. I would like to generate an automatic ID based on parent class. I'm using a method to create Airport, so my ID it's coming with is a null value. ID in AirportModel will be generated, but I don't know how to make it in child class.
#Entity(name = "Airport")
#Table(name = "ai_airport")
public class AirportModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "airport_id")
private List<AirportTranslatedModel> translations;
Second class(child):
#Entity(name = "AirportTranslated")
#IdClass(AirportTranslatedModelKey.class)
#Table(name = "ai_translated_airport")
public class AirportTranslatedModel
#Id
#Column(name="airport_id")
private Long airportId;
#Id
#Column(name="language_code", length=2)
private String languageCode;
Third one(keys):
#Embeddable
public class AirportTranslatedModelKey implements Serializable {
#Column(name="airport_id")
private Long airportId;
#Column(name="language_code", length=2)
private String languageCode;
I still got the same errors; log:
Hibernate: insert into ai_airport (active, airport_code, city_code, country_code, externa
l_id, is_default, latitude, longitude, market_code, min_connection_time_DD, min_connection_time_DI, min_connection_time_id, min_connection_time_II, time_diff, VERSION) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into ai_translated_airport (airport_long_name, airport_short_name, airp
ort_id, language_code) values (?, ?, ?, ?)
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Column 'airport_id' cannot be null
Your current setup has the AirportTranslatedModel airport_id field mapped through a Long- you will need to set the airportId manually to have it set the id in the database. This will likely require that you persist AirportModel and possibly flush to have its PK assigned and available prior to making the AirportModel->AirportTranslatedModel association, so that you can then set the AirportTranslatedModel.airportId.
JPA 2 though allows derived Ids. If you want AirportTranslatedModel to have its ID assigned from AirportModel, it needs to have a relationship to it. There is a simple example at http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/DerivedIdentifiers
If you were to model your classes in a similar fashion, it might look like:
public class AirportModel {
..
#OneToMany(mappedby="airportModel", cascade = CascadeType.ALL, orphanRemoval = true)
private List<AirportTranslatedModel> translations;
..
}
public class AirportTranslatedModel {
#Id
#JoinColumn(name="airport_id")
private AirportModel airportModel;
#Id
#Column(name="language_code", length=2)
private String languageCode;
..
}
public class AirportTranslatedModelKey implements Serializable {
private Long airportModel;
private String languageCode;
}
Notice that there is no need to make the AirportTranslatedModelKey and embeddable if you are just using it as a pk class. Also note that the AirportTranslatedModelKey contains a Long airportModel - this must match the type of the pk in the AirportModel, and the name of the relationship property in AirportTranslatedModel.
This will allow AirportTranslatedModel to pull the airport_id value from AirportModel and use it as its PK even though it might not have been generated yet when both entities are still new.