I'm using JPA 2 with Eclipselink 2 and a Derby in memory db for my tests. When I start my little test programm I get the following exception:
[EL Warning]: 2010-08-12 17:17:44.943--ServerSession(948252856)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: 'ALTER TABLE' cannot be performed on 'ARTICLE_ARTICLE' because it does not exist.
Error Code: 30000
Call: ALTER TABLE ARTICLE_ARTICLE DROP CONSTRAINT RTCLrtclsRltdTThsD
Query: DataModifyQuery(sql="ALTER TABLE ARTICLE_ARTICLE DROP CONSTRAINT RTCLrtclsRltdTThsD")
If I try the same with HyperSQL (hsqldb) I get:
[EL Warning]: 2010-08-12 17:47:33.179--ServerSession(1925661675)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: user lacks privilege or object not found: ARTICLE_ARTICLE
Error Code: -5501
Call: ALTER TABLE ARTICLE_ARTICLE DROP CONSTRAINT FK_ARTICLE_ARTICLE_articlesRelatedToThis_ID
Query: DataModifyQuery(sql="ALTER TABLE ARTICLE_ARTICLE DROP CONSTRAINT FK_ARTICLE_ARTICLE_articlesRelatedToThis_ID")
The table generation strategy is "drop-and-create", so why does Eclipselink tell me that a table would not exist?
Or is something wrong with my example class?
#Entity
public class Article implements Serializable {
#Id #GeneratedValue
private int id;
private String title;
#ManyToMany(mappedBy = "relatedArticles")
private Set<Article> articlesRelatedToThis = new HashSet<Article>();
#ManyToMany(cascade = CascadeType.PERSIST)
private Set<Article> relatedArticles = new HashSet<Article>();
public Article() {}
public Article(String title) {
this.title = title;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Set<Article> getRelatedArticles() {
return relatedArticles;
}
public void addRelatedArticle(Article related) {
relatedArticles.add(related);
}
public void removeRelatedArticle(Article related) {
relatedArticles.remove(related);
}
#PreRemove
public void preRemove() {
for(Article article : articlesRelatedToThis)
article.removeRelatedArticle(this);
}
}
The table generation strategy is "drop-and-create", so why does Eclipselink tell me that a table would not exist?
EcliseLink starts by dropping table constraints (the alter statement you're seeing), then the tables, and then recreate everything. Since you are using an in-memory database, there is actually nothing to drop and EclipseLink reports the failed attempts as a warning. Just ignore them.
Related
How can I determine from the error message from Hibernate / Oracle which entity caused the problem when batch inserting entities.
Is there a way setup Hibernate or Oracle to log this information?
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute batch
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:124)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:122)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:101)
at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:161)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:207)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:390)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:303)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:349)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:67)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1166)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1223)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268)
... 375 more
Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (FHIR.SYS_C0022940074) violated
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:11190)
at oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:244)
at org.jboss.jca.adapters.jdbc.WrappedStatement.executeBatch(WrappedStatement.java:1077)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:113)
... 386 more
Assuming we have the following JPA entity:
#Entity(name = "Post")
#Table(name = "post")
public class Post {
#Id
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
The most important thing to note about this entity is that the entity identifier must be assigned manually.
Now, let's emulate a batch processing that will always end up throwing a ConstraintViolationException:
Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> {
try (PreparedStatement st = connection.prepareStatement(
"INSERT INTO post (id, title) " +
"VALUES (?, ?)")) {
for (long i = 0; i < 5; i++) {
st.setLong(1, i % 2);
st.setString(2, String.format("High-Performance Java Persistence, Part %d", i));
st.addBatch();
}
st.executeBatch();
} catch (BatchUpdateException e) {
LOGGER.info("Batch has managed to process {} entries", e.getUpdateCounts().length);
}
});
Because we assign the identifier using the modulo operator, the third entry will fail to be inserted since it will clash with the firsts row that we saved into the database.
So, when running the aforementioned test case, this is what we get logged:
c.v.b.h.h.b.BatchExceptionTest - testInsertPosts
n.t.d.l.SLF4JQueryLoggingListener - Name:DATA_SOURCE_PROXY, Time:0,
Success:False,
Type:Prepared,
Batch:True,
QuerySize:1,
BatchSize:5,
Query:[
"INSERT INTO post (id, title) VALUES (?, ?)"],
Params:[
(0, High-Performance Java Persistence, Part 0),
(1, High-Performance Java Persistence, Part 1),
(0, High-Performance Java Persistence, Part 2),
(1, High-Performance Java Persistence, Part 3),
(0, High-Performance Java Persistence, Part 4)
]
c.v.b.h.h.b.BatchExceptionTest - Batch has managed to process 2 entries
So, to answer the question, you need to use the BatchUpdateException#getUpdateCounts method to know how many items you managed to processed successfully, and so the next one is the one that caused the failure.
I have the following classes:
Hardware.java
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Hardware{
#Id
#GeneratedValue(strategy=GenerationType.TABLE)
private long id;
private String serialnumber;
#OneToMany (mappedBy="hardware")
private List<Equipment> equipments;
}
Computer.java
#Entity
public class Computer extends Hardware {
private String hostname;
private String macAdress;
private String ipNumber;
public Computer(){
super();
}
public Computer(String serialnumber, String hostname,
String macAdress, String ipNumber) {
super(serialnumber);
this.hostname = hostname;
this.macAdress = macAdress;
this.ipNumber = ipNumber;
}
}
NetworkDeivce.java
#Entity
public class NetworkDevice extends Hardware {
private String hostname;
private String ipAdress;
public NetworkDevice(){
super();
}
public NetworkDevice(String serialnumber, String hostname,
String ipAdress) {
super(serialnumber);
this.hostname = hostname;
this.ipAdress = ipAdress;
}
}
And now the class that is mapped to the Hardware Class:
Equipment.java
#Entity
public class Equipment {
public Equipment(){
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String serialnumber;
private String info;
#ManyToOne (fetch=FetchType.EAGER)
#JoinColumn(name="HW_ID")
private Hardware hardware;
}
Now if I add a Equipment to a Computer it all works finde, but if I try to add Equipment to a NetworkDevice I get this Error:
Internal Exception:
Internal Exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
Error Code: 1452
Call: INSERT INTO EQUIPMENT (INFO, SERIALNUMBER, HW_ID) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(domain.hardware.Equipment#600a620d)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:102)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at main.dbTest(main.java:74)
at main.main(main.java:18)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
Error Code: 1452
Call: INSERT INTO EQUIPMENT (INFO, SERIALNUMBER, HW_ID) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(domain.hardware.Equipment#600a620d)
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:851)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:913)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`inventorytool_beta`.`EQUIPMENT`, CONSTRAINT `FK_EQUIPMENT_HW_ID` FOREIGN KEY (`HW_ID`) REFERENCES `COMPUTER` (`ID`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
So I think it's a problem with the inheritance... I'm not very good at JPA..
Thanks in advance.
Have a look at the schema that was generated. You should had three different tables for hardware, due to your inheritance strategy: one for Hardware, one for Computer and one fon NetworkDevice. The problem is that HW_ID can only reference one table. Here your JPA provider chose Computer, probably because it's first in alphabetical order, but it can't handle all three classes.
Consider using another inheritance strategy, like JOIN.
Check the code where you insert Equipment to a NetworkDevice. It looks like HW_ID does not exists in the master-table(Hardware). Because it is trying to insert nonexistent Master-Table HW_ID as foreign-Key in child table(Equipment).
Regards,
Ravi
Yes it comes from your inheritance strategy. You can't use #GeneratedValue(strategy=GenerationType.IDENTITY) with table_per_class.
See Java/Hibernate JPA: InheritanceType.TABLE_PER_CLASS and IDs
Im using JPa API's and its work well ,I have tried to add new member/column to the class(table) and when I was tried to add data for it works fine but in the commit part I get dump with the following error
Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: 'DOUBLE1' is not a column in table or VTI 'TEST.PERSON'.
Error Code: 20000
Call: INSERT INTO PERSON (ID, DOUBLE1, FIRSTNAME, LASTNAME, NONSENSEFIELD) VALUES (?, ?, ?, ?, ?)
bind => [5 parameters bound]
But in the table person I have added the member double1 as follows
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private String id;
private String firstName;
private String lastName;
private double double1;
....
public double getDouble1() {
return double1;
}
public void setDouble1(double double1) {
this.double1 = double1;
}
What am i missing here?
There is obviously no column DOUBLE1 in the database table VTI 'TEST.PERSON'. Adding a new field to a JPA entity does not automatically make it appear in the database as well.
I have Eclipselink persistence provider tuned on DB2 DB. Where is 3 tables which simplified definition are listed below:
CREATE TABLE root
(
id CHAR(32) NOT NULL PRIMARY KEY,
rec_type VARCHAR(20)
);
CREATE TABLE derived
(
id CHAR(32) NOT NULL PRIMARY KEY,
...
);
ALTER TABLE derived ADD CONSTRAINT fk_derived_to_root FOREIGN KEY (id) REFERENCES root(id);
CREATE TABLE secondary
(
derived_id NOT NULL PRIMARY KEY,
...
);
ALTER TABLE secondary ADD CONSTRAINT fk_secondary_to_derived FOREIGN KEY (derived_id) REFERENCES derived(id);
Java entity classes for these entities are listed below,
RootEntity:
#javax.persistence.Table(name = "ROOT")
#Entity
#DiscriminatorColumn(name = "REC_TYPE")
#Inheritance(strategy = InheritanceType.JOINED)
public class RootEntity {
private String id;
#javax.persistence.Column(name = "ID")
#Id
#GeneratedValue(generator = "system-uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String principalType;
#Column(name = "PRINCIPAL_TYPE")
public String getPrincipalType() {
return principalType;
}
public void setPrincipalType(String principalType) {
this.principalType = principalType;
}
...
}
DerivedEntity:
#javax.persistence.Table(name = "DERIVED")
#Entity
#DescriminatorValue("DERIVED")
public class DerivedEntity extends RootEntity {
private SecondaryEntity secondaryEntity;
#OneToOne(mappedBy = "derived_id")
public SecondaryEntity getSecondaryEntity() {
return secondaryEntity;
}
public void setSecondaryEntity(SecondaryEntity secondaryEntity) {
this.secondaryEntity = secondaryEntity;
}
...
}
I see no derived table insertion in the test logs:
--INSERT INTO ROOT (ID, REC_TYPE) VALUES (?, ?)
bind => [241153d01c204ed79109ce658c066f4c, Derived]
--INSERT INTO SECONDARY (DERIVED_ID, ...) VALUES (?, ...)
bind => [241153d01c204ed79109ce658c066f4c, ...]
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.ibm.db2.jcc.am.fo: DB2 SQL Error: SQLCODE=-530, SQLSTATE=23503, SQLERRMC=SCHEM.SECONDARY.FK_SECONDARY_TO_DERIVED, DRIVER=3.57.82
So question is: why Eclipselink don't insert new record into DERIVED table prior to insertion to SECONDARY table?
P.S. Everything is working fine when no SECONDARY table (ROOT and DERIVED tables only) or no inheritance used (DERIVED tables generates id).
For inheritance JPA assumes the foreign key constraints in related table refer to the root table.
You can change your constraint to refer to the root table, or,
use a DescriptorCustomizer to set,
descriptor.setHasMultipleTableConstraintDependecy(true);
or,
customizer the OneToOneMapping to have its foreign key refer to the secondary table (JPA annotation always make it refer to the root table).
Please log a bug though, as JPA join columns should allow you to define a foreign key to the secondary table.
The reason that EclipseLink does defer the insert into the secondary table is to allow inserts to be grouped by tables to allow batch writing and avoid database deadlocks.
I am building JPA based application using mysql and ecliselink.I have very strange issue when try to insert stuff into my database.I am able to insert data into single table but when it comes to one-to-may and vice versa something goes wrong.Currently I have 2 main and 1 reference table(it holds the foreign keys of the other two tables).It is strange because I dont have "sequence" in my database table When I try to insert data into any of my tables I get this exception:
[EL Info]: 2012-03-15 17:52:28.64--ServerSession(18621340)--EclipseLink, version: Eclipse Persistence Services - 2.3.2.v20111125-r10461
[EL Info]: 2012-03-15 17:52:29.23--ServerSession(18621340)--file:/D:/git-eclipse/Martin/reference/build/classes/_reference login successful
[EL Warning]: 2012-03-15 17:52:29.389--ClientSession(31843177)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'eclipse1.sequence' doesn't exist
Error Code: 1146
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 = ?")
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'eclipse1.sequence' doesn't exist
Error Code: 1146
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 = ?")
[EL Info]: 2012-03-15 17:52:29.394--ServerSession(18621340)--file:/D:/git-eclipse/Martin/reference/build/classes/_reference logout successful
Exception in thread "main" java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManager.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.verifyOpen(EntityManagerImpl.java:1665)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.close(EntityManagerImpl.java:1529)
at OneToManyRelation.main(OneToManyRelation.java:47)
I am posting one class only because others are quite similar
#Entity
#Table(name="category")
public class Category {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="CategoryID")
private int CategoryID;
/**
* #return the id
*/
public int getId() {
return CategoryID;
}
/**
* #param id the id to set
*/
public void setId(int CategoryID) {
this.CategoryID = CategoryID;
}
#Column(name="category", nullable=false, length=50, insertable=true)
private String category;
/**
* #return the category
*/
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name = "templateemail", joinColumns = {
#JoinColumn(name="categoryId", unique = true)
},
inverseJoinColumns = {
#JoinColumn(name="templateId")
}
)
private Set<Template> template;
/**
*
*/
public Set<Template> getChildren() {
return template;
}
/**
*
*/
public void setChildren(Set<Template> template) {
this.template = template;
}
}
Do you have any idea what is wrong with my code?
Thanks in advance
Seeing the code would help finding what's wrong with it. But by judging on the error message only, it seems you chose to use a sequence or table generator, and that this generator relies (by default) on a table named sequence, that doesn't exist in the database.
Create this table, or configure the generator to use an existing table, or change the ID generator.