Hibernate: one-to-many and inheritance, table not getting created - java

I made an one-to-many relationchip between two classes (that actually works). But as soon as I add a second class which extends one of that classes the table dont get created anymore. I cant figure out why.
Shortly:
Class A got a 1-to-Many relation to Class C,
Class B extends Class A
#Entity
#Table(name = "PRODUCT")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="discriminator",discriminatorType=DiscriminatorType.STRING )
#DiscriminatorValue(value="P")
public class Product{
#Id
#GeneratedValue
#Column(name = "PRODUCT_ID")
private int productID;
#Column(name = "ARTICLE_NUMBER")
private String articleNumber;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "TITLE")
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name="MANUFACTURER_TITLE")
private Manufacturer manufacturer;
//Constructor, getter and setter
}
Subclass:
#Entity
#Table(name="PRODUCT")
#DiscriminatorValue("E")
public class Service extends Product{
#Column(name = "INTERVAL")
private int interval;
#Column(name = "UNITS")
private String units;
//Constructor, getter and setter
}
Relationchip to:
#Entity
#Table(name = "MANUFACTURER")
public class Manufacturer implements Serializable {
#Id
#Column(name = "MANUFACTURER_TITLE")
private String title;
#OneToMany(mappedBy="manufacturer")
private Set<Product> products;
//Constructor, getter and setter
}
Mapping:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testshop</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"/>
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">1500</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!--Autocreate Tables-->
<!--property name="hibernate.archive.autodetection" value="class"/-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!--Debug show sql-->
<property name="show_sql">true</property>
<!--Character Encoding for Using UTF-8 to use german special characters-->
<property name="hibernate.connection.CharSet">utf8</property>
<property name="hibernate.connection.characterEncoding">utf8</property>
<property name="hibernate.connection.useUnicode">true</property>
<!--Entity to be mapped-->
<!--Caching for performance-->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<mapping class="test.Department"/>
<mapping class="test.Person"/>
<mapping class="test.Employee"/>
<mapping class="main.ShopConfig"/>
<mapping class="main.Logging"/>
<mapping class="product.Product"/>
<mapping class="product.Service"/>
<mapping class="manufacturer.Manufacturer"/>
</session-factory>
</hibernate-configuration>
A soon as I add the mapping for the service class it doesnt work anymore..
Edit:
If I delete the one-to-many relationchip the table gets created.. but why?!
Edit2:
Hibernate Error:
16-09-2014 10:55:14 ERROR SchemaExport:425 - HHH000389: Unsuccessful: create table PRODUCT (discriminator varchar(31) not null, PRODUCT_ID integer not null auto_increment, ARTICLE_NUMBER varchar(255), DESCRIPTION varchar(255), END_DATE tinyblob, PRICE double precision, PUBLISHED bit, QUANTITY integer, START_DATE tinyblob, TITLE varchar(255), UNLIMITED bit, WEIGHT varchar(255), INTERVAL integer, UNITS varchar(255), MANUFACTURER_TITLE varchar(255), primary key (PRODUCT_ID))
10705 [http-bio-8080-exec-12] ERROR org.hibernate.tool.hbm2ddl.SchemaExport - HHH000389: Unsuccessful: create table PRODUCT (discriminator varchar(31) not null, PRODUCT_ID integer not null auto_increment, ARTICLE_NUMBER varchar(255), DESCRIPTION varchar(255), END_DATE tinyblob, PRICE double precision, PUBLISHED bit, QUANTITY integer, START_DATE tinyblob, TITLE varchar(255), UNLIMITED bit, WEIGHT varchar(255), INTERVAL integer, UNITS varchar(255), MANUFACTURER_TITLE varchar(255), primary key (PRODUCT_ID))
16-09-2014 10:55:14 ERROR SchemaExport:426 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTERVAL integer, UNITS varchar(255), MANUFACTURER_TITLE varchar(255), primary k' at line 1
10706 [http-bio-8080-exec-12] ERROR org.hibernate.tool.hbm2ddl.SchemaExport - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTERVAL integer, UNITS varchar(255), MANUFACTURER_TITLE varchar(255), primary k' at line 1

The problem is with the field 'interval'. It is a reserved word in MySql. Change it to something else.

Related

How to find records with Foreign Key using Criteria Query. It's hibernate One-to-One Bidirectional mapping with Foreign Key done using hbm.xml

I have two entities namely Student and Address. The parent Entity being Student and the Child entity being Address. Considering that each student can have only one Address and each Address can belong to only one student, this makes it a One-to-One bi-directional mapping.
I am trying to retrieve Address records based on the Foreign Key(studentId). I need to write a criteria query for this. Since in a One-to-one bidirectional mapping we don't declare the Foreign key as a field in the Address Entity class, I am unable to add Restrictions, which otherwise if added would query for the records based on the Foreign key. Looking for Help in writing Criteria Query to Fetch Address records based on Student ID.
public class Student{
private String studentId; //pk
private String firstName;
private String lastName;
private Address address;
//getters and setters
}
public class Address{
private String addressId; //pk
private String streetAddress;
private String addressLine1;
private String addressLine2;
private String city;
private String state;
private String country;
private String zipCode;
private Student student
//getters and setters
}
public interface AddressDao{
//Find Address based on ForeignKey
public Address findByStudentId(String studentId)
}
public class AddressDaoImpl{
#Override
public Address findByStudentId(String studentId) {
Criteria criteria = createCriteria();
criteria.add(Restrictions.eq("Should be the column name of the Field holding Foreign key", studentId)); // haven't declared the student ID field in the Address POJO
Object result = criteria.uniqueResult();
return result == null ? null : (Address) result;
}
STUDENT TABLE
CREATE TABLE STUDENT (
ID VARCHAR(20) PRIMARY KEY,
FIRST_NAME VARCHAR(30) NOT NULL,
LAST_NAME VARCHAR(30) NOT NULL
);
ADDRESS TABLE
CREATE TABLE ADDRESS (
A_ID VARCHAR(20) PRIMARY KEY,
STREET_ADDRESS VARCHAR(20),
ADDR1 VARCHAR(20) NOT NULL,
ADDR2 VARCHAR(20) NOT NULL,
CITY VARCHAR(20)NOT NULL,
STATE VARCHAR(20)NOT NULL,
COUNTRY VARCHAR(20) NOT NULL,
ZIPCODE INTEGER(5)NOT NULL,
ID VARCHAR(20) UNIQUE,
FOREIGN KEY(ID) REFERENCES STUDENT(ID)
);
Student hbm.xml
<hibernate-mapping>
<class name="Student" table="STDT">
<id name="studentId" column="ID"</id>
<property name="name" column="NAME" update="false" type="string" />
<property name="firstName" column="FIRST_NAME"/>
<property name="lastName" column="LAST_NAME"/>
<one-to-one name="address" cascade="all"></one-to-one>
</class>
<hibernate-mapping>
Address hbm.xml
<hibernate-mapping>
<class name="Address" table="ADDRESS">
<id name="addressId" column="A_ID"</id>
<property name="streetAddress" column="STREET_ADDRESS"/>
<property name="addressLine1" column="ADDR1"/>
<property name="addressLine2" column="ADDR2"/>
<property name="city" column="CITY"/>
<property name="state" column="STATE"/>
<property name="country" column="COUNTRY"/>
<property name="zipCode" column="ZIPCODE"/>
<many-to-one name="student" column="ID" unique="true"
not-null="true" lazy="false" />
</class>
<hibernate-mapping>
See here: [https://stackoverflow.com/a/1787161/333296]
You should be able to get the address of a student with student.id.

Unique constraint error on inserting data

I am getting strange error Caused by:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint
While executing my below code:
Product DAO.java
#Id
#Column(name = "no", columnDefinition = "NUMBER")
private int serial_number;
//No getter and setter for this field
#Column(name = "fname", columnDefinition = "VARCHAR2(50)")
private int fname;
#Column(name = "lname", columnDefinition = "VARCHAR2(50)")
private int lname;
// Getter and setter for fname and lname
ProductService.java
Product po = new Product();
po.setfname = "Tom";
po.setlname = "John";
//I am not setting 'no' field value since I have created sequence in my oracle table to auto increment the value.
When I am running this code, I am getting unique constraint error on field 'no'. Can anyone help me in identifying what I am doing wrong in my code. When I have already created sequence for 'no' field in my table, do I need to make any change in config file or code? Since its the production database, I do not know the sequence name also.
hibernate-cgf.xml
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:#localhost:1521:xe</property>
<property name="hibernate.connection.password">pass</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping class="dao.Product"></mapping>
</session-factory>
</hibernate-configuration>
Your id field serial_number is an int which is initialized to zero, and your mapping for #Id does not include a #GeneratedValue annotation, so hibernate will assume you are assigning the id manually and save it as zero every time you persist an object, causing the SQLIntegrityConstraintViolationException. You need to add a #GeneratedValue annotation and you can also choose a strategy, like this:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "no", columnDefinition = "NUMBER")
private int serial_number;

Hibernate + MySQL simple batch insert extremely slow

I'm inserting 2500 records from Hibernate into a totally empty MySQL table. The insert is taking 5 minutes!
I've googled for hours and tried a few things like an autogenerated primary key but nothing seems to improve the performance.
An earlier version of my program was doing inserts concurrently (1 per thread with ~100 threads) and that was taking ~2 minutes. I thought batching should improve performance by ~10x but it seems to have backfired.
I'm using Google Cloud's MySQL with a db-f1-micro instance
This is what my table looks like (only table in the DB!):
CREATE TABLE `categories` (
`browse_node` varchar(60) NOT NULL,
`name` varchar(60) DEFAULT NULL,
`path` varchar(400) DEFAULT NULL,
`url` varchar(200) NOT NULL,
`level` int(11) NOT NULL,
PRIMARY KEY (`browse_node`)
)
This is the POJO:
package example.com;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Represents a category from the categories table
*/
#Entity
#Table(name = "categories")
public class Category {
#Id
#Column(name = "browse_node")
private String browseNode;
#Column(name = "name")
private String name;
#Column(name = "path")
private String path;
#Column(name = "url")
private String url;
#Column(name = "level")
private int level;
public Category() {
}
public Category(String browseNode, String name, String path, String url, int level) {
this.browseNode = browseNode;
this.name = name;
this.path = path;
this.url = url;
this.level = level;
}
// Omitting setters/getters
}
Here's the code doing the insertion:
private static void writeCategoriesToDb(Map<String, Category> categories) {
StatelessSession session = sessionFactory.openStatelessSession();
// Session session = sessionFactory.openSession();
session.beginTransaction();
int i = 0;
int batchSize = 50;
for (Category category : categories.values()) {
session.insert(category);
// if (i % batchSize == 0) {
// session.flush();
// session.clear();
// }
// i++;
}
session.getTransaction().commit();
session.close();
}
And here's the config file:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://someIp/myDB</property>
<property name="connection.username">root</property>
<property name="connection.password">password</property>
<property name="connection.useSSL">false</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">20</property>
<property name="hibernate.jdbc.batch_size">3000</property>
<property name="hibernate.id.new_generator_mappings">false</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<mapping class="example.com.Category"/>
</session-factory>
</hibernate-configuration>
Found the answer here.
Adding rewriteBatchedStatements=true to my JDBC url fixed it!
It now takes ~2.2 seconds to insert all the records.
<property name="connection.url">jdbc:mysql://someIp/myDB?rewriteBatchedStatements=true</property>

Issue in persisting entity with #EmbeddedId and #ElementCollection in Postgres using Hibernate

I want to perform CRUD operation over Postgres 9 using Hibernate.
Entity:
#Entity
#Table(name = "MESSAGE_HISTORY_RECORD")
public class MessageHistoryRecord {
#EmbeddedId
private MessageCompoundKey compoundKey;
#Column
private String responseChannel;
#ElementCollection
private List<Trace> traces;
#Column
private byte[] payload;
//getters and setters
}
Composite Id entity:
#Embeddable
public class MessageCompoundKey implements Serializable {
private static final long serialVersionUID = 9084329307727034214L;
#Column
private String correlatedMsgId;
#Column
private String messageId;
#Column
private String endpointId;
//getters and setters
}
ElementCollection Entity:
#Embeddable
public class Trace implements Serializable{
private static final long serialVersionUID = 9084329307727034214L;
private Long timestamp;
private String description;
//getters and setters
}
I am using hibernate.hbm2ddl.auto=update to create schema for me.
It created tables for me:
CREATE TABLE "public"."message_history_record"
(
correlatedmsgid varchar(255) NOT NULL,
endpointid varchar(255) NOT NULL,
messageid varchar(255) NOT NULL,
payload bytea,
responsechannel varchar(255),
CONSTRAINT message_history_record_pkey PRIMARY KEY (correlatedmsgid,endpointid,messageid)
)
;
CREATE UNIQUE INDEX message_history_record_pkey ON "public"."message_history_record"
(
correlatedmsgid,
endpointid,
messageid
)
;
CREATE TABLE "public"."messagehistoryrecord_traces"
(
messagehistoryrecord_correlatedmsgid varchar(255) NOT NULL,
messagehistoryrecord_endpointid varchar(255) NOT NULL,
messagehistoryrecord_messageid varchar(255) NOT NULL,
description varchar(255),
timestamp bigint
)
On persisting any object, I did not find any entry in messagehistoryrecord_traces table.
Hibernate properties:
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.connection.url=jdbc:postgresql://192.xx.xx.xx:5432/testdb
hibernate.connection.username=***
hibernate.connection.password=****
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.pool_size=10
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
Persist sql :
Hibernate: insert into MESSAGE_HISTORY_RECORD (payload, responseChannel, correlatedMsgId, endpointId, messageId) values (?, ?, ?, ?, ?)
According to your configuration the defaults should apply for the table name, column names and join column names of the collection table. These defaults are constructed as follows:
Table name: name of the referencing entity, appended with an underscore and the name of the entity attribute that contains the element colletion ( MessageHistoryRecord_traces)
Join column: name of the referencing entity, appended
with an underscore and the name of the primary key column of the entity table.
This second case is only allowed if you have a single primary key field in the parent entity which is not the case in your case. So you have specify the join column yourself as follows (I renamed the collection table name and foreign key column names because they are too long for my database system):
#ElementCollection
#CollectionTable(name = "mhr_traces",
joinColumns={#JoinColumn(name="mhr_correlatedmsgid", referencedColumnName="correlatedmsgid"),
#JoinColumn(name="mhr_endpointid", referencedColumnName="endpointid"),
#JoinColumn(name = "mhr_messageid", referencedColumnName = "messageid")})
private List<Trace> traces = new ArrayList<>();
And one more thing: you have to implement the equals() and hashCode() methods for the primary key class if you haven't done yet (they are not visible in your post).
Your table creation script is also missing the foreign key definitino (add them manually if they are not generated automatically):
CONSTRAINT mrFK FOREIGN KEY (mhr_correlatedmsgid, mhr_endpointid, mhr_messageid) REFERENCES MESSAGE_HISTORY_RECORD (correlatedmsgid,endpointid,messageid)
Adjust it matching to your database syntax (I don't know PostgreSQL)
With these adjustments everything works for me; indeed on an Oracle database system and EclipseLink as persistence provider. I think it is not implementation specific
Did you add anything to your traces list or it was empty??
It is working for me with postgresql without any tweaks. With hbm2ddl.auto set to update, hibernate created the tables and foreign key relationship between them as well. Here is the sample code I used :
public class App
{
public static void main( String[] args )
{
System.out.println("Maven + Hibernate + Postgresql");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
MessageCompoundKey cKey = new MessageCompoundKey();
cKey.setCorrelatedMsgId("correlatedMsgId_2");
cKey.setEndpointId("endpointId_2");
cKey.setMessageId("messageId_2");
MessageHistoryRecord record = new MessageHistoryRecord();
record.setResponseChannel("ArsenalFanTv");
List<Trace> traces = new ArrayList<>();
Trace t1 = new Trace();
t1.setDescription("description_1");
t1.setTimestamp(System.currentTimeMillis());
traces.add(t1);
Trace t2 = new Trace();
t2.setDescription("description_2");
t2.setTimestamp(System.currentTimeMillis());
traces.add(t2);
record.setCompoundKey(cKey);
record.setTraces(traces);
session.save(record);
session.getTransaction().commit();
}
}
and my configuration file (hibernate.cfg.xml) is as follows :
<hibernate-configuration>
<session-factory>
<!-- <property name="hibernate.bytecode.use_reflection_optimizer">false</property> -->
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.password">****</property>
<property name="hibernate.connection.url">jdbc:postgresql://127.0.0.1:5432/testdb</property>
<property name="hibernate.connection.username">****</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping class="com.skm.schema.MessageHistoryRecord"></mapping>
</session-factory>
</hibernate-configuration>
I was using StatelessSession instead of session. As per the documentation:
A stateless session does not implement a first-level cache nor interact with any second-level cache, nor does it implement transactional write-behind or automatic dirty checking, nor do operations cascade to associated instances. Collections are ignored by a stateless session. Operations performed via a stateless session bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable to data aliasing effects, due to the lack of a first-level cache.
More details can be found on this thread.
After using Session instead of StatelessSession, it worked.

Entity Class is a non Entity?

I got the following Error:
[class model.VerkaufterArtikel] uses a non-entity [class model.Verkauf] as target entity in the relationship attribute [field verkauf].
But the class is listed in the persistence.xml. And I tried also to not exlude unlisted classes.
I'm using EclipseLink as JPA Implementation.
I tried to exlude the relation but then I was not able to persist Verkauf. The other classes are working correctly.
The situation is: I have a sale(Verkauf) wich contains sold items(VerkaufteArtikel).
model.Verkauf:
package model;
#Entity
#Table(name = "verkauf")
#NamedQuery(name = "Verkauf.findAll", query = "SELECT v FROM Verkauf v")
public class Verkauf implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int verkID;
private int gegeben;
#OneToMany(mappedBy="verkauf")
private List<VerkaufterArtikel> verkaufArtikels = new ArrayList<VerkaufterArtikel>();
model.VerkaufterArtikel:
#Entity
#Table(name="verkauf_artikel")
#NamedQuery(name="VerkaufArtikel.findAll", query="SELECT v FROM VerkaufterArtikel v")
public class VerkaufterArtikel implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private int id;
private float anz;
private int artID;
private int preispro;
#ManyToOne
#JoinColumn(name="verkaufID")
private Verkauf verkauf;
My persistence XML:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="dbm" transaction-type="RESOURCE_LOCAL">
<!-- <exclude-unlisted-classes>false</exclude-unlisted-classes>-->
<class>model.Artikel</class>
<class>model.ArtikelInWarenkorb</class>
<class>model.VerkaufterArtikel</class>
<class>model.Verkaeufer</class>
<class>model.Verkauf</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mdb" />
<property name="javax.persistence.jdbc.user" value="user" />
<property name="javax.persistence.jdbc.password" value="password" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="eclipselink.allow-zero-id" value="true" />
</properties>
</persistence-unit>
</persistence>
And the Tables:
CREATE TABLE `verkauf_artikel` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`verkaufID` INT(11) NULL DEFAULT NULL,
`artID` INT(11) NULL DEFAULT NULL,
`anz` FLOAT NULL DEFAULT NULL,
`preispro` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
CREATE TABLE `verkauf` (
id` INT(11) NOT NULL AUTO_INCREMENT,
`verkID` INT(11) NULL DEFAULT '0',
`zeit` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gegeben` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
Thank you!
I think your #ManyToOneMapping is not correct, it should look like this:
#ManyToOne
#JoinColumn(name="verkID")
private Verkauf verkauf;
I finally found the reason for this error.
I had a getSum() method at the bottom of the class. I removed the method and everything is working fine now!
public int getSum() {
return (int) getVerkaufArtikels()
.stream()
.mapToDouble(acc -> ((double) acc.getPreispro() * acc.getAnz()))
.sum();
}

Categories

Resources