Hibernate crashes when using more than one #OneToMany entries in my Users.entity and I don't understand why.
I have a table for users (primary key userID) and various other tables which refer to the primary key userID by a foreign key set in database (InnoDB set and foreign key is set in each depending table).
Here an example with three tables:
Table users:
CREATE TABLE `users` (`userID` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `users` ADD PRIMARY KEY (`userID`);
Table: vacationrequests
CREATE TABLE `vacationrequests` (`requestID` int(11) NOT NULL,`userID` int(4) NOT NULL,) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `vacationrequests` ADD CONSTRAINT `userID` FOREIGN KEY (`userID`) REFERENCES `users` (`userID`) ON UPDATE CASCADE; COMMIT;
Table monthend:
CREATE TABLE `monthend` (`monthendID` int(11) NOT NULL,`userID` int(4) NOT NULL,) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Entity Users:
#Entity
#Table(name="users")
public class Users {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="userID")
private int userID;
... .... (other variables)
#OneToMany(fetch = FetchType.LAZY, mappedBy="monthend")
private Set<Monthend> monthend;
#OneToMany(fetch = FetchType.LAZY, mappedBy="vacationrequests")
private Set<Vacationrequests> vacationrequests;
public Users() {
}
Entity Vacationrequests:
#Entity
#Table(name="vacationrequests")
public class Vacationrequests {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="requestID")
private int requestID;
... .... (other variables)
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="userID", nullable = false)
private Users users;
public Vacationrequests() {
}
Entity Monthend:
#Entity
#Table(name="monthend")
public class Monthend {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="monthendID")
private int monthendID;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="userID", nullable = false)
private Users users;
#Column(name="clientID")
private int clientID;
#Column(name="month")
private int month;
#Column(name="year")
private int year;
#Column(name="monthend")
private int monthend;
public Monthend() {
}
Working Query:
List<Object> listJoinUsersMonthend = session.createQuery("from Monthend m inner join m.users as users where m.clientID = :clientID AND m.year = :year AND users.loginclass = 0 AND users.isactive = 1 AND m.monthend = 0 AND m.month < :month order by users.userID asc")
.setInteger("clientID", user.getClientID())
.setInteger("year", year)
.setInteger("month", month)
.getResultList();
This second query i would like to integrate:
List<Object> listJoinVacationrequestsUsers = session.createQuery("from Vacationrequests v inner join v.users as users where v.clientID = :clientID AND v.approved=0 AND v.vacationtype=1 AND YEAR(v.startdate) = :year" )
.setInteger("clientID", user.getClientID())
.setInteger("year", year)
.getResultList();
Everything works fine with just one query and one entry in the Users.entity. As soon as I add the two entries hibernate just crashes and there is no error message. Is it not possible to do two #OneToMany statements in a entity ?
It seems that the problem is in the mapping of the associations in the Users entity.
You should change the mappedBy attribute of the associations to specify the field name that references the current entity in the associated one (in this case users), like this:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "users")
private Set<Monthend> monthend;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "users")
private Set<Vacationrequests> vacationrequests;
Related
I am writing a simple inventory database that contains tables for products, orders and customers. The database definition can be found here:
CREATE TABLE public.customers
(
id integer NOT NULL DEFAULT nextval('customers_id_seq'::regclass),
title character varying(10) COLLATE pg_catalog."default" NOT NULL,
first_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
middle_names character varying(50) COLLATE pg_catalog."default",
last_name character varying(50) COLLATE pg_catalog."default" NOT NULL,
email character varying(50) COLLATE pg_catalog."default" NOT NULL,
phone_number character varying(50) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT customers_pkey PRIMARY KEY (id)
)
CREATE TABLE public.products
(
id integer NOT NULL DEFAULT nextval('products_id_seq'::regclass),
name character varying(100) COLLATE pg_catalog."default" NOT NULL,
sku integer NOT NULL,
inventory_on_hand integer NOT NULL,
reorder_threshold integer NOT NULL,
price numeric(5,2),
inventory_to_be_shipped integer NOT NULL,
CONSTRAINT products_pkey PRIMARY KEY (id)
)
CREATE TABLE public.order_items
(
id integer NOT NULL DEFAULT nextval('order_items_id_seq'::regclass),
product_id integer NOT NULL,
order_id integer NOT NULL,
CONSTRAINT order_items_pkey PRIMARY KEY (id),
CONSTRAINT order_items_order_id_fkey FOREIGN KEY (order_id)
REFERENCES public.orders (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT order_items_product_id_fkey FOREIGN KEY (product_id)
REFERENCES public.products (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
CREATE TABLE public.orders
(
id integer NOT NULL DEFAULT nextval('orders_id_seq'::regclass),
customer_id integer,
order_date date NOT NULL DEFAULT now(),
arrival_date date,
CONSTRAINT orders_pkey PRIMARY KEY (id),
CONSTRAINT orders_customer_id_fkey FOREIGN KEY (customer_id)
REFERENCES public.customers (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
I am trying to implement a Spring Security Resource server to perform CRUD operations on the database. I have implemented entity classes for each table in the database but when try to start the server I get a
org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: edu.finalyearproject.imsresourceserver.models.Order.customers in edu.finalyearproject.imsresourceserver.models.Customer.orders
My entity and repository classes can be found below:
Product.java:
#Entity
#Table(name = "products")
#Data
public class Product
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer sku;
private Float price;
private Integer inventory_on_hand;
private Integer reorder_threshold;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
Customer.java
#Entity
#Table(name = "customers")
#Data
public class Customer
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String title;
private String first_name;
private String middle_names;
private String last_name;
private String email;
private String phone_number;
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
Order.java
#Entity
#Table(name = "orders")
#Data
public class Order
{
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name="customer_id", nullable=false)
private Customer customer;
private Date order_date;
private Date arrival_date;
#ManyToMany(mappedBy = "orders", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Product> products = new HashSet<>();
}
I know the problem is related to the relationships between the entities, but I haven't been able to find a solution. Any help would be greatly appreciated.
Try to correct this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "orders", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
to this:
#Entity
public class Customer
{
// ...
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Order> orders;
}
See additional explanation in the documentation.
And you should correct also your Product-Order #ManyToMany association. Only one side of this association should use #JoinTable other side should use mappedBy property of the #ManyToMany annotation. Something like this:
#Entity
public class Product
{
// ...
#ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY
)
#JoinTable(
name = "order_items",
joinColumns = #JoinColumn(name = "product_id"),
inverseJoinColumns = #JoinColumn(name = "order_id")
)
private Set<Order> orders = new HashSet<>();
}
#Entity
public class Order
{
// ...
#ManyToMany(
mappedBy = "orders",
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY)
private Set<Product> products = new HashSet<>();
}
As it is stated in the documentation:
For #ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
Also as this is explained in this section of the documentation:
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those which, in turn, can lead to N+1 query issues.
For this reason, you should prefer LAZY associations.
I have 2 tables in database side(oracle)
create table GROUPS
(
ID NUMBER not null,
GROUP_NAME VARCHAR2(30)
)alter table GROUPS
add constraint ID primary key (ID)
and
create table ITEM_GROUP
(
ITEM_ID VARCHAR2(30) not null,
GROUP_ID NUMBER not null
)
alter table ITEM_GROUP
add constraint ITEM_GROUPD_ID primary key (ITEM_ID, GROUP_ID)
alter table ITEM_GROUP
add constraint ITEM_GROUP_FK01 foreign key (GROUP_ID)
references GROUPS (ID);
Than I have mapping classes in Java side. I want to make thing, when I am selecting group to take all his items too, and I want to save item with hibernate it is all .
#Entity
#Table(name = "GROUPS")
public class Group {
#Id
#Column(name = "ID", nullable = false)
#javax.persistence.SequenceGenerator(name = "groupIdGenerator", sequenceName = "GROUP_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "groupIdGenerator")
private int id;
#Column(name = "GROUP_NAME")
private String groupName;
#JsonManagedReference
#OneToMany(fetch = FetchType.EAGER, mappedBy="group",cascade = CascadeType.ALL)
private List<GroupItems> groupItems = new ArrayList<>();
// setters and getters
}
#SuppressWarnings("serial")
#Embeddable
public class GroupItemPK implements Serializable {
#Column(name = "ITEM_ID")
private String merchantId;
#Column(name = "GROUP_ID")
private int id;
// getters , setters , constructors , equals hashcode methods
}
#Entity
#Table(name = "ITEM_GROUP")
public class GroupITEM {
#EmbeddedId
private GroupITEMtPK id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID")
#JsonBackReference
private Group group;
}
I am interested in did i make any mistakes in build relationship ? If I did what is my mistakes , because I can not do my select and save queries without exceptions.
I am trying to do in my Code
List<Group> list = sessionFactory.getCurrentSession().createQuery("from Group a").list();
and here is my Exception
org.hibernate.engine.jdbc.spi.SqlExceptionHelper could not extract ResultSet [n/a]
java.sql.SQLSyntaxErrorException: ORA-00904: "GROUPITE0_"."ID": invalid identifier
I have below two tables:
`message` (
`id` varchar(150),
`Message` varchar(1000) ,
PRIMARY KEY (`id`)
)
fallback_status (
message_key varchar(150),
fallback_delivere` tinyint(1),
fallback_read tinyint(1),
fallback_answered tinyint(1),
PRIMARY KEY (message_key)
)
I don't have any foreign key relationship at database level. I have created two entity for these tables
#Entity
#Table(name = "message")
public class Message {
#Id
#Column(name = "id", unique = true)
private String key;
#Column(name = "message")
String message
#OneToOne
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "message_key")
private MessageFallbackStatus messageFallbackStatus;
}
and
#Entity
#Table(name = "fallback_status")
public class MessageFallbackStatus {
#Id
#Column(name="message_key")
private String messageKey;
#Column(name="fallback_delivered")
private boolean fallbackDelivered;
#Column(name="fallback_read")
private boolean fallbackRead;
#Column(name="fallback_answered")
private boolean fallbackAnswered;
}
With this configuration Hibernate not able to persist data in fallback_status table.
my questions are:
Is the foreign key relationship is mandatory at database level?
what would be the proper mapping for this scenario?
thanks for any help.
I have three tables to query with Hibernate or JPA...
Tables structure
CREATE TABLE `product` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`insert_date` datetime NOT NULL,
`last_update` datetime NOT NULL,
...
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;
CREATE TABLE `language` (
`language_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
...
PRIMARY KEY (`language_id`),
KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `product_description` (
`product_id` int(11) unsigned NOT NULL,
`language_id` int(11) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`description` text NOT NULL,
`tag` text NOT NULL,
`meta_title` varchar(255) NOT NULL,
`meta_description` varchar(255) NOT NULL,
`meta_keyword` varchar(255) NOT NULL,
PRIMARY KEY (`product_id`,`language_id`),
KEY `name` (`name`),
KEY `product_language_idx` (`language_id`),
CONSTRAINT `product_description` FOREIGN KEY (`product_id`) REFERENCES `product` (`product_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `product_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Product <OneToMany> ProductDescription
Language <ManyToOne> ProductDescription
Assuming my entities are the following:
Product Entity:
#Entity
#Table(name="product")
#NamedQuery(name="Product.findAll", query="SELECT p FROM Product p")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="product_id", unique=true, nullable=false)
private int productId;
....
//bi-directional many-to-one association to ProductDescription
#OneToMany(fetch = FetchType.LAZY, mappedBy = "product")
private List<ProductDescription> productDescriptions;
Language Entity:
#Entity
#Table(name="language")
#NamedQuery(name="Language.findAll", query="SELECT l FROM Language l")
public class Language implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="language_id", unique=true, nullable=false)
private int languageId;
...
//bi-directional many-to-one association to ProductDescription
#ManyToOne(fetch=FetchType.LAZY)
//#JoinColumn(name="language_id", referencedColumnName="id",nullable=false, insertable=false, updatable=false)
private ProductDescription productDescription;
ProductDescription Entity (id is a composite key in class ProductDescriptionPK):
#Entity
#Table(name="product_description")
#NamedQuery(name="ProductDescription.findAll", query="SELECT p FROM ProductDescription p")
public class ProductDescription implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ProductDescriptionPK id;
//bi-directional many-to-one association to Language
#OneToMany(mappedBy="productDescription")
private List<Language> languages;
//bi-directional many-to-one association to Product
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="product_id", nullable=false, insertable=false, updatable=false)
private Product product;
So I have to execute a query as sample, using a join between product and product_description, where language is 1:
SELECT p.product_id, pd.description
FROM product p
INNER JOIN product_description pd
USING ( product_id )
INNER JOIN language
USING ( language_id )
WHERE language_id = 1
Firstly I have used Hibernate with Criteria Query:
Criteria criteria = getSession().createCriteria(Product.class,"p");
criteria.createAlias("p.productDescriptions", "pd");
criteria.createAlias("pd.languages", "l");
criteria.add(Restrictions.eq("l.languageId", new Integer(1)));
result = criteria.list();
But that code retrieves this error:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Unknown column 'l2_.productDescription_language_id' in 'field list'
What's my issue? How can I perform queries like this?
Thanks a lot!
I do not understand completely your data model.
I think that the relationship between a Language and a ProductDescription should be one-to-many from the Language point of view but putting that aside ...
UPDATED:
Effectively, Hibernate do not map correctly the relationships using the annotations that you have indicated above. It's trying to map the strange ManyToOne relation in the table language and it can not find those fields: productDescription_language_id and productDescription_product_id.
I think that the correct mapping for your tables is:
LANGUAGE ENTITY
#Entity
public class Language {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="language_id", unique=true, nullable=false)
private Long languageId;
private String name;
#OneToMany(fetch=FetchType.LAZY, mappedBy="language")
private List<ProductDescription> productDescriptions =
new ArrayList<ProductDescription>();
// Other fields + getters and setters
}
PRODUCT DESCRIPTION ENTITY
#Entity
#Table(name="product_description")
public class ProductDescription {
#Embeddable
public static class ProductDescriptionPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "product_id")
protected Long productId;
#Column(name = "language_id")
protected Long languageId;
public ProductDescriptionPK() {
}
public ProductDescriptionPK(Long productId, Long languageId) {
super();
this.productId = productId;
this.languageId = languageId;
}
}
#EmbeddedId
private ProductDescriptionPK id;
private String description;
#ManyToOne
#JoinColumn(name="language_id", nullable=false, insertable=false, updatable=false)
private Language language;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="product_id", nullable=false, insertable=false, updatable=false)
private Product product;
// Other fields + getters and setters
}
Here is a working example of how you can chain joins with JPQL using the entities defined as you have declared them on your question.
EntityManager em = emf.createEntityManager();
// [UPDATED] QUERY
String jpql = "SELECT p.id, pd.description FROM Product p "
+ "JOIN p.productDescriptions pd "
+ "JOIN pd.language l WHERE l.language_id = :idLanguage)";
Query query = newEm.createQuery(jpql);
query.setParameter("idLanguage", new Long(1));
List<Object> resultList = query.getResultList();
System.out.println( resultList.size() + " product(s) found:" );
for (Object singleResult : resultList) {
Object[] singleRow = (Object[]) singleResult;
System.out.println(singleRow[0] + " " + singleRow[1]);
}
That code generates this SQL query [UPDATED]
select
product0_.product_id as col_0_0_,
productdes1_.description as col_1_0_
from
Product product0_
inner join
product_description productdes1_
on product0_.product_id=productdes1_.product_id
inner join
Language language2_
on productdes1_.language_id=language2_.language_id
where
language2_.language_id=?
I have been reading some articles and books on the subject and using a left join fetch with a where clause is invalid. Quoting "Java Persistence with Hibernate" by Gavin King et al:
"The query left join fetch i.bids b where b.amount ... is invalid. You can't say, "Load the Item instances and initializes their bids collections, but only with Bid instances that have a certain amount"
Hope this helps.
I am using hibernate 3 and attempting to delete a record in the database, and the delete is not working as I would expect. The schema hibernate is working against (in pseudocode):
create table Employer(
employer_id number(12) primary key,
employer_name varchar2(50)
);
create table Employee(
employee_id number(12) primary key,
employee_name varchar2(50),
employer_id number(12) foreign key references employer.employer_id not null
);
create table Employee_Roles(
role_id number(12) primary key,
employee_id number(12) foreign key references employee.employee_id not null,
role varchar2(50)
);
My hibernate class mappings look something like:
#Entity
public class Employer{
#Id
#Column(name = "EMPLOYER_ID")
private long id;
#Column
private String name;
#OneToMany(targetEntity = Employee.class, fetch = FetchType.EAGER)
#JoinColumn(name = "employer_id")
#Cascade(CascadeType.ALL)
private Set<Employee> employees;
}
#Entity
public class Employee{
#ManyToOne(targetEntity = Employer.class)
#JoinColumn(name = "employer_id")
#Cascade(value = CascadeType.SAVE_UPDATE)
private Employer employer;
#OneToMany(targetEntity = EmployeeRole.class, fetch = FetchType.EAGER)
#JoinColumn(name = "employee_id")
#Cascade(CascadeType.ALL)
private Set<Employee> employees;
}
#Entity
public class EmployeeRole{
#ManyToOne(targetEntity = Employee.class)
#JoinColumn(name = "employee_id")
#Cascade(value = CascadeType.SAVE_UPDATE)
private Employee employee;
}
Now with this configuration I am calling:
getCurrentSession().delete(someEmployerEntity);
What is occurring is:
Hibernate: update EMPLOYEE set EMPLOYEE_ID=null where EMPLOYEE_ID=?
Hibernate: update EMPLOYEE_ROLE set employee_id=null where employee_id==?
[2011-04-15 15:59:53,487] JDBCExceptionReporter WARN - SQL Error: -10, SQLState: 23502
[2011-04-15 15:59:53,487] JDBCExceptionReporter ERROR - integrity constraint violation: NOT NULL check constraint; SYS_CT_10058 table: EMPLOYEE_ROLE
and an exception being raised. What I am expecting as a result of the session.remove(..) call is the employer record to be deleted, as well as all employee records associated with the employer and all EmployeeRole records associated with the deleted employee records. Is this a correct assumption? Or am I misunderstanding a key concept here?
Cascade all-delete-orphan should solve your problem. However, it is part of Hibernate and not EJB standard. If you want to do it and do not be trapped in your vendors' solution, I would suggest you to have a look to this article.
Good luck!
EDIT: following your suggestions I added the 'mappedBy' attributes to the #OneToMany annotations which seems to be the annotations way of using inverse="true" for specifying the owning relationships. The relevant changed sections of the relationships look like:
public class Employee{
#OneToMany(targetEntity = EmployeeRole.class, mappedBy="employee", fetch = FetchType.EAGER, cascadeType=CascadeType.ALL)
private Set<EmployeeRole> employeeRoles;
}
public class Employer{
#OneToMany(targetEntity = Employee.class, mappedBy="employer", fetch = FetchType.EAGER, cascadeType=CascadeType.ALL)
private Set<Employee> employees;
}