I'm trying to optimize my JPA implementation, using EclipseLink.
I've added batch operations to it. But it is still taking A LOT of time to do 50 000 inserts. It takes more than 10 times the amount of time it takes to do the exact same insert using raw SQL with JDBC.
To make sure batch operations were in fact working I used Wireshark to check my packets and it is not using batch inserts.
Here's one of the insert packets:
It is not doing:
INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0'), ('sfirosijfhgdoi 0', 'dsufius0')... and so on
I was expecting it to do as above but it is inserting one line per packet and not multiple lines per packet.
Here's is my Entity Class:
#Entity
public class EntityClassTest implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String lastname;
public EntityClassTest() {
}
public EntityClassTest(Long id, String name, String lastname) {
this.id = id;
this.name = name;
this.lastname = lastname;
}
public EntityClassTest(String name, String lastname) {
this.name = name;
this.lastname = lastname;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getLastName() {
return lastname;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setLastName(String lastname) {
this.lastname = lastname;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof EntityClassTest)) {
return false;
}
EntityClassTest other = (EntityClassTest) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "database.EntityClassTest [id=" + id + " ]";
}
}
And here is my persist method that receives a List and persists all the objects inside.
public void insertListToTable(final String persistenceUnit, final List list) throws SQLException {
final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnit);
final EntityManager entityManager = entityManagerFactory.createEntityManager();
final EntityTransaction transaction = entityManager.getTransaction();
try {
final int listSize = list.size();
transaction.begin();
for (int i = 0; i<listSize; i++) { //Object object : list) {
final Object object = list.get(i);
entityManager.persist(object);
if ( i % 500 == 0 ) { //500, same as the JDBC batch size defined in the persistence.xml
//flush a batch of inserts and release memory:
entityManager.flush();
entityManager.clear();
}
}
transaction.commit();
}
catch(Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw new SQLException(e.getMessage());
}
finally {
entityManager.close();
}
}
And my persistence.xml, where I set 500 as the batch value, file is:
<?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="ExternalServer" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<!-- List of Entity classes -->
<class>model.EntityClassTest</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://myServer:3306/testdb?zeroDateTimeBehavior=convertToNull"/>
<property name="javax.persistence.jdbc.user" value="testdbuser"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.password" value="myPassword"/>
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<!-- Weaving -->
<property name="eclipselink.weaving" value="static"/>
<!-- SQL dialect / Database type -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="eclipselink.target-database" value="MySQL"/>
<!-- Tell the JPA provider to, by default, create the table if it does not exist. -->
<property name="javax.persistence.schema-generation.database.action" value="create"/>
<!-- No logging (For development change the value to "FINE") -->
<property name="eclipselink.logging.level" value="OFF"/>
<!-- Enable batch writing -->
<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
<!-- Batch size -->
<property name="eclipselink.jdbc.batch-writing.size" value="500"/>
</properties>
</persistence-unit>
</persistence>
So my question is, why is it not batch inserting? I believe I've EclipseLink well configured to do so from what I've been reading around on EclipseLink website and here SO as well.
//////////////////////////// EDIT //////////////////////////
As suggested by Chris's answer, I changed in my EntityClassTest this value #GeneratedValue(strategy = GenerationType.IDENTITY) to #GeneratedValue(strategy = GenerationType.SEQUENCE) and re-run the test and the packets are being sent as before (like the image I posted above). So it didn't fix my problem I'm afraid.
//////////////////////////// EDIT 2 ////////////////////////
I've changed the logging level in the persistence.xml file to FINEST as shown next.
<property name="eclipselink.logging.level" value="FINEST"/>
And here is the log generated. I placed it in a pastebin because it is quite long.
http://pastebin.com/rKihCKMW
It seems to be calling Execute query InsertObjectQuery quite a lot of times.
//////////////////////////// EDIT 3 ////////////////////////
Here's the version for each component I'm using.
+-------------------------+------------------------------+
| Variable_name | Value |
+-------------------------+------------------------------+
| innodb_version | 5.6.12 |
| protocol_version | 10 |
| slave_type_conversions | |
| version | 5.6.12-log |
| version_comment | MySQL Community Server (GPL) |
| version_compile_machine | x86_64 |
| version_compile_os | Win64 |
+-------------------------+------------------------------+
Netbeans 8.0
EclipseLink (JPA 2.1)
mysql-connector-java-5.1.24.jar
//////////////////////////// EDIT 4 ////////////////////////
Following CuriousMind's answer I've edited my EntityClassTest id annotation to:
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator="id-seq-gen")
#SequenceGenerator( name="id-seq-gen", sequenceName="ID_SEQ_GEN", allocationSize=500 )
private Long id;
But it didn't solve my problem, I'm still getting one single insert per packet (as described by the image above) and on the EclipseLink log I'm getting:
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.251 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2068, dsufius1034]
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--SELECT LAST_INSERT_ID()
[EL Finest]: sequencing: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--assign sequence to the object (1.252 -> database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--UnitOfWork(1985011414)--Thread(Thread[main,5,main])--Execute query InsertObjectQuery(database.EntityClassTest [id=null ])
[EL Finest]: query: 2014-10-19 06:44:02.608--ClientSession(824177287)--Thread(Thread[main,5,main])--Execute query ValueReadQuery(name="ID_SEQ_GEN" sql="SELECT LAST_INSERT_ID()")
[EL Fine]: sql: 2014-10-19 06:44:02.608--ClientSession(824177287)--Connection(1674390738)--Thread(Thread[main,5,main])--INSERT INTO ENTITYCLASSTEST (LASTNAME, NAME) VALUES (?, ?)
bind => [sfirosijfhgdoi 2244, dsufius1122]
And so on...
You are using GenerationType.IDENTITY for sequencing, which requires retrieving the IDs from each insert statement one by one. Try a sequencing scheme that allows preallocation in batches of 500 and you will see improvements:
http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Identity_sequencing
It's been one year already, probably it's too late to answer. In my case, I found that flush() cause the problem. I call flush() after persist() for each record. This prevent batch writing to make an optimization and results in poor insert performance. After removing flush(), everything went well.
It seems the sequence generation has been causing the issue, you refer to this post. It provides approach of preallocated sequence generation.
This could be becuase of the sequence/indentity (need to use preallocation), but also for mysql I think you need a connection string property to allow bulk insert:
rewriteBatchedStatements=true
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
Related
Well, there are many questions exactly with this title but none of them have proper answers or they are not exactly the same as mine.
I have two entities:
Person:
#Entity
#Table(name = "Person")
#Inheritance(strategy = InheritanceType.JOINED)
#Access(AccessType.FIELD)
public class Person {
#Id
#GeneratedValue
private Long id;
#Column(name = "firstname")
private String firstName;
#Column(name = "lastname", length = 100, nullable = false, unique = false)
private String lastName;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.MERGE, mappedBy="owner")
private Set<Car> cars;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Set<Car> getCars() {
return cars;
}
public void setCars(Set<Car> cars) {
this.cars = cars;
}
#Override
public String toString() {
return String.format("(%d, %s, %s)",id, firstName, lastName);
}
}
And Car:
#Entity
#Table(name = "Car")
#Inheritance(strategy = InheritanceType.JOINED)
#Access(AccessType.FIELD)
public class Car {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumn(name="id_person", columnDefinition="BIGINT")
private Person owner;
#Column(name="name")
private String name;
#Column(name="model")
private String model;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#Override
public String toString() {
return String.format("(%d, %s, %s, %s)", id, name, model, owner);
}
}
Also I've defined a foreign key constraints in my Mysql database between Person and Car table on id_person column
My persistence.xml file is as followings:
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!-- Configuring JDBC properties -->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/hibernatedb" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="DA_PASSWORD" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<!-- Hibernate properties -->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
<!-- Configuring Connection Pool -->
<property name="hibernate.c3p0.min_size" value="5" />
<property name="hibernate.c3p0.max_size" value="20" />
<property name="hibernate.c3p0.timeout" value="500" />
<property name="hibernate.c3p0.max_statements" value="50" />
<property name="hibernate.c3p0.idle_test_period" value="2000" />
</properties>
</persistence-unit>
</persistence>
Inside my code, I try to select Cars using Criteria query as following:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Car> q = cb.createQuery(Car.class);
Root<Car> root = q.from(Car.class);
q.select(root);
When I get the results and print them
TypedQuery<Car> typedQuery = entityManager.createQuery(q);
List<Car> cars = typedQuery.getResultList();
log.info("*****************************{}", cars);
To my surprise, It prints:
*****************************[(1, Hiundai, 2016, (1, Homer1530962140, Simpson)), (2, Benz, 2016, (1, Homer1530962140, Simpson)), (3, Benz,
2017, (2, Homer12935192, Simpson))]
That means for each car items, owners eagerly have been fetched too!
Here are the database query logs:
2017-02-17T14:02:58.324926Z 391 Query /* mysql-connector-java-5.1.13 ( Revision: ${bzr.revision-id} ) */SELECT ##session.auto_increment_increment
2017-02-17T14:02:58.325405Z 391 Query SHOW COLLATION
2017-02-17T14:02:58.335552Z 391 Query SET NAMES latin1
2017-02-17T14:02:58.335772Z 391 Query SET character_set_results = NULL
2017-02-17T14:02:58.336160Z 391 Query SET autocommit=1
2017-02-17T14:02:58.336349Z 391 Query SET autocommit=0
2017-02-17T14:02:58.720821Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Car'
2017-02-17T14:02:58.724527Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Car'
2017-02-17T14:02:58.725337Z 391 Query SHOW FULL COLUMNS FROM `Car` FROM `hibernatedb` LIKE '%'
2017-02-17T14:02:58.729899Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Person'
2017-02-17T14:02:58.730468Z 391 Query SHOW FULL TABLES FROM `hibernatedb` LIKE 'Person'
2017-02-17T14:02:58.730887Z 391 Query SHOW FULL COLUMNS FROM `Person` FROM `hibernatedb` LIKE '%'
2017-02-17T14:02:59.022835Z 391 Query select car0_.id as id1_0_, car0_.model as model2_0_, car0_.name as name3_0_, car0_.id_person as id_perso4_0_ from Car car0_
2017-02-17T14:02:59.041016Z 391 Query SHOW WARNINGS
2017-02-17T14:02:59.045266Z 391 Query select person0_.id as id1_1_0_, person0_.firstname as firstnam2_1_0_, person0_.lastname as lastname3_1_0_ from Person person0_ where person0_.i
d=1
2017-02-17T14:02:59.059184Z 391 Query SHOW WARNINGS
2017-02-17T14:02:59.064163Z 391 Query select person0_.id as id1_1_0_, person0_.firstname as firstnam2_1_0_, person0_.lastname as lastname3_1_0_ from Person person0_ where person0_.i
d=2
2017-02-17T14:02:59.065827Z 391 Query SHOW WARNINGS
2017-02-17T14:02:59.070262Z 391 Query rollback
2017-02-17T14:02:59.070468Z 391 Quit
It is quite obvious that a separate query is issued to get Person information while I don't seem to ask such a thing in my code.
Why is this happening?
You requested the owner information when you converted the car to a string.
#Override
public String toString() {
return String.format("(%d, %s, %s, %s)", id, name, model, owner);
}
At that point, it had to retrieve the owner to execute your toString().
log.info("*****************************{}", cars);
When you execute this:
Logging framework calls toString() on a list
toString() is called on each Car
toString method of the Car class calls toString() method of the Person class. And this is the part where "magic" happens. Turns out that Hibernate uses generated proxy classes to enable lazy loading for some associations (one-to-one, many-to-one). In other words, Hibernate initializes field Car.owner with a reference to a proxy class that extends the Person class. This proxy class contains additional logic that handles lazy loading.
I am using plain java to hibernate standalone application with Hibernate Envers for getting updates of changes made in table's columns, I am using sql server as my Database, and I am new in envers.
Here is my "CustomRevisionEntity.java"
#Entity
#AuditTable("REVINFO")
#RevisionEntity(CustomRevisionListener.class)
public class CustomRevisionEntity {
#Column (name = "USERNAME", length = 50)
private String username;
#Id
#GeneratedValue
#RevisionNumber
#Column (name = "REV", unique = true, nullable = false)
private int id;
#Temporal(TemporalType.DATE)
#Column (name = "REVTSTMP", nullable = false, length = 15)
#RevisionTimestamp
private Date timestamp;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
CustomRevisionListener.java
public class CustomRevisionListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
CustomRevisionEntity revision = (CustomRevisionEntity) revisionEntity ;
String userName = Hibernate_Connection.getloggedUser();
revision.setUsername(userName);
}
}
Hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">jdbc:sqlserver://localhost:1433;instance=SQLEXPRESS_2012;DatabaseName=ETS_V11_DEV;integratedSecurity=true</property> -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> -->
<property name="show_sql">true</property>
<property name="use_sql_comments">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping class= "Domain_hibernate_SQLServer.Domain"/>
<mapping class= "Domain_hibernate_SQLServer.CustomRevisionEntity"/>
</session-factory>
</hibernate-configuration>
Problem: While using hbm.xml file then it is adding value on username column,
but while I am using Annotation for getting value that time is taking null value as it is not recognizing extra column property that I have added, but
While using annotation, its inserting null values in username columns
It is taking values like this with annotation while seeing sql code on console
/* insert org.hibernate.envers.DefaultRevisionEntity
*/ insert
into
REVINFO
(REVTSTMP)
values
(?)
Table has only 3 columns, 1 is REV, i.e, autoincrement, 2nd is REVTSTMP, nd 3rd is USERNAME, and Its not taking username,
What I am missing, If you need more information then please comment
I think the problem comes from your annotation config so can post your hbm.xml file and the class used for the anotation config?
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>
I am trying to make a add to wish list feature for my app but I keep getting this error:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'WISH_ID' cannot accept a NULL value.
Error Code: -1
Call: INSERT INTO WISHLIST (BOOK_TITLE, CUSTOMER_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: InsertObjectQuery(dukesbookstore.entity.Wishlist[ wishId=null ])
ENTITY class:
#Entity
#Table(name = "WISHLIST")
#XmlRootElement
#NamedQueries(
{
#NamedQuery(name = "Wishlist.findByCustomerId", query = "SELECT w FROM Wishlist w WHERE w.customerId = :customerId"),
})
public class Wishlist implements Serializable
{
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "WISH_ID")
private Integer wishId;
#Column(name = "CUSTOMER_ID")
private Integer customerId;
#Size(max = 35)
#Column(name = "BOOK_TITLE")
private String bookTitle;
public Wishlist()
{
}
public Integer getCustomerId()
{
return customerId;
}
public void setCustomerId(Integer customerId)
{
this.customerId = customerId;
}
public String getBookTitle()
{
return bookTitle;
}
public void setBookTitle(String bookTitle)
{
this.bookTitle = bookTitle;
}
}
and this is the code for creating a new wish:
public void createWishlist(String title,int cust_id)
{
Wishlist newWish = new Wishlist();
newWish.setBookTitle(title);
newWish.setCustomerId(cust_id);
em.persist(newWish);
}
I tried to look at other similar problems but they involves hibernate which i am not using. I have also tried various generation strategy such as AUTO,SEQUENCE,TABLE but all failed. I also have another entity named customer which is exactly same but it works fine though its created from a form.
Changing to AUTO generates this error:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: Table/View 'SEQUENCE' does not exist.
Error Code: -1
Call: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
Query: DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?")
root cause
java.sql.SQLSyntaxErrorException: Table/View 'SEQUENCE' does not exist.
root cause
org.apache.derby.client.am.SqlException: Table/View 'SEQUENCE' does not exist.
Persistence.xml incase relevant
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="myStorePU" transaction-type="JTA">
<jta-data-source>mydb</jta-data-source>
<class>myStore.entity.Book</class>
<class>myStore.entity.Customer</class>
<class>myStore.entity.Wishlist</class>
<properties>
<property name="javax.persistence.jdbc.user" value="APP"/>
<property name="javax.persistence.jdbc.password" value="APP"/>
<property name="eclipselink.logging.level" value="FINE"/>
</properties>
</persistence-unit>
Finally it is working now,
firstly, i had to delete the table that I created from netbeans db creation tool, which had a creation code like this, as seen by using "grab structure"
create table "APP".WISHLIST
(
WISH_ID NUMERIC(5) not null primary key,
CUSTOMER_ID NUMERIC(5),
BOOK_TITLE VARCHAR(100)
)
secondly, I added this code into my persistence.xml file
<property name="eclipselink.ddl-generation" value="create-tables"/>
This solved the problem as it created the table by it self, which have different creation code, as seen from its grab structure from auto creation:
create table "APP".WISHLIST
(
WISH_ID INTEGER default GENERATED_BY_DEFAULT not null primary key,
CUSTOMER_ID NUMERIC(5),
BOOK_TITLE VARCHAR(100)
)
So, basically should let netbeans create the table itself from entity but i was using "Create entity from table" features, for that i had to create the tables first in netbeans gui.
Thank you #Geziefer for all the help, I learned quite a bit from your help too.
This might be a "Non nullable attributes" in JPA single-table-inheritance problem. It might help and doesn't hurt to specify
#JoinTable(name = "[some join table name]",
joinColumns = {#JoinColumn(name = "[some ID column name]")},
inverseJoinColumns = {#JoinColumn(name = "[some different ID column name]")})
I'm trying to set up a web application using JBoss and Hibernate, but I can't get the SQL database running, encountering several problems.
My main problem is that when calling persist() Hibernate tries to insert an empty object into my table, the log is starting with this exception:
12:39:26,985 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (http--127.0.0.1-8080-1) HHH000228: Running hbm2ddl schema update
12:39:26,986 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (http--127.0.0.1-8080-1) HHH000102: Fetching database metadata
12:39:26,987 ERROR [org.hibernate.tool.hbm2ddl.SchemaUpdate] (http--127.0.0.1-8080-1) HHH000319: Could not get database metadata: java.sql.SQLException: You cannot set autocommit during a managed transaction!
at org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnection.setJdbcAutoCommit(BaseWrapperManagedConnection.java:888)
After using Google I couldn't really figure out how to solve it, but my application continues running. Somewhere after this Hibernate seems to try to call persist() to a created object.
12:39:27,202 INFO [stdout] (http--127.0.0.1-8080-1) Hibernate:
12:39:27,202 INFO [stdout] (http--127.0.0.1-8080-1) insert
12:39:27,203 INFO [stdout] (http--127.0.0.1-8080-1) into
12:39:27,203 INFO [stdout] (http--127.0.0.1-8080-1) Person
12:39:27,203 INFO [stdout] (http--127.0.0.1-8080-1) (id, birthdate, gender, name, password)
12:39:27,204 INFO [stdout] (http--127.0.0.1-8080-1) values
12:39:27,204 INFO [stdout] (http--127.0.0.1-8080-1) (null, ?, ?, ?, ?)
Using Logger I saw that this Person object is correctly created, but as you can see Hibernate sees the values as (null, ?, ?, ?, ?).
After this this exception is thrown:
12:39:27,218 ERROR [org.apache.tapestry5.ioc.Registry] (http--127.0.0.1-8080-1) org.hibernate.exception.ConstraintViolationException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10031 table: PERSON column: ID
So then, my persistence.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="facePlace">
<non-jta-data-source>java:jboss/facePlace</non-jta-data-source>
<class>webtech2.faceplace.entities.Person</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
The relevant code is:
#Inject
#Persistence
EntityManager em;
public boolean signUp(String name,
String password,
String repeatPassword,
Date birthdate,
String gender) {
if (!password.equals(repeatPassword)) {
return false;
}
log.info("person data: " + name + " " + password + " " + repeatPassword + " " + birthdate.toString() + " " + gender);
String saltedPassword = hashText + password;
String hashedPassword = generateHash(saltedPassword);
em.getTransaction().begin();
Person xperson = new Person(name, hashedPassword, birthdate, gender);
em.persist(xperson);
em.getTransaction().commit();
return true;
}
My entity looks like:
#Entity
public class Person implements Serializable {
private String name;
private Date birthdate;
private long id;
private String password;
private String gender;
private Set<Person> friends;
#Id
#GeneratedValue
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
So I'm not an expert with all this, someone sees some shit in there?
Looks like your primary key is not being set when hibrenate saves the file. the #Id column in your #Entity should specify a primary key generation strategy, rather than leaving #GeneratedValue without any paramaters and having hibernate trying to pick a default generation startegy.
If you are using an identity column in the database you set.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="pkey")
private Integer pkey;