Mysql inserting values - java

I am storing an Object into my database, and I think I am making some stupid mistake which I don't see. I attached some code to understand my problem:
MyObject.java object which I want to store in the database
#Entity
#Table(name = "my_object")
public class MyObject {
private String idName;
#Id
#Column(name = "id", unique = true, length = 50,nullable=false)
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
this.id = idName.hashCode();
}
TestToStoreObject.java
MyObject obj = new MyObject();
obj.setIdName("id");
//More set ...
save(obj);
Function to store the Object into the database
protected void save(MyObject obj) throws DataAccessLayerException {
try{
getHibernateTemplate().save(obj);
} catch (HibernateException e) {
handleException(e);
}
}
This is a general function which is working with other datatypes. So where I guess the problem is, is in configuration files. The exception is:
org.springframework.dao.InvalidDataAccessResourceUsageException:
could not insert: [path.MyObject]; SQL [insert into object (id) values (?)];
nested exception is org.hibernate.exception.SQLGrammarException:
could not insert: [path.MyObject]
Here more configuration files:
HibernatePlatform.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<mapping class="path.User" />
<mapping class="path.MyObject" />

Just a guess - you've defined a class named Object and there's a good chance, that you have name clashes between your class Object and the java class java.lang.Object. It's possible, that you try to store an java.lang.Object accidently.
For a quick test: refactor your class to MyObject (using eclipse, netbeans, ...). If some types named Object are not changed after refactoring, then my guess could be right.

The problem was that I had another class with the same name in another package, and it was messing. A piece of advise for the future: chach this small things!!

Related

How do I properly set up JPA with Intellj for a postgresql database and oracle persistence provider to get rid of runtime exceptions?

I am totally new to JPA and try to get at it with the tools provided from my University. So what I am trying to do is to set up a database carsdb with one table car and read the table from my main with a JPQL Query.
What I did so far:
I created a user carsdbuser with password carsdbpw, created a postgres database carsdb, which is owned by carsdbuser, added a table car and inserted a few columns.
I created a new Intellij project with JPA.
I then added the postgresql jdbc drivers (I think... the file was given to us by the university) postgresql-42.2.1.jar
as well as the oracle provider eclipslink.jar.
I then modified the persistence.xml to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="carsdb" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>Car</class>
<properties>
<property name="eclipselink.jdbc.url" value="jdbc:postgresql://localhost:5432/carsdb"/>
<property name="eclipselink.jdbc.driver" value="org.postgresql.Driver"/>
<property name="eclipselink.jdbc.user" value="carsdbuser"/>
<property name="eclipselink.jdbc.password" value="carsdbpw"/>
<property name="eclipselink.target-database" value="PostgreSQL"/>
<property name="eclipselink.logging.level" value="ALL"/>
</properties>
</persistence-unit>
</persistence>
I created the Class Car.java:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "car")
public class Car {
#Id
#GeneratedValue(generator = "incrementor")
private int Id;
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
private String Name;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
#Override
public String toString() {
return "Car " + Id + ": Name: " + Name;
}
}
as well as the Main:
import javax.persistence.*;
import java.util.List;
public class Main {
public static void main(String[] args){
var factory = Persistence.createEntityManagerFactory("carsdb");
EntityManager em = factory.createEntityManager();
Query query = em.createQuery("select a from Car a");
List<Car> list = query.getResultList();
for (Car c : list) {
System.out.println(c);
}
}
}
Given the tutorials I am following this looks good to me, however when I run the program I get the following Error Message:
Exception in thread "main" java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [select a from Car a].
[14, 17] The abstract schema type 'Car' is unknown.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1743)
at Main.main(Main.java:9)
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.1.v20171221-bd47e8f): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Problem compiling [select a from Car a].
[14, 17] The abstract schema type 'Car' is unknown.
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:155)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:347)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.populateQueryImp(HermesParser.java:278)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildQuery(HermesParser.java:163)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:140)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:116)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:102)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:86)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1741)
... 1 more
I tried to find some solutions with the help of some classmates and google, but did not find any solution that helped.
I added the Database in Intellij to make sure I got the right url. The connection test works properly and I also find my car table in the Intellij database view.
The following Question discusses a similar Issue:
Error on compiling query: The abstract schema type 'entity' is unknown
I do however have Car in the select statement which is the case sensitive name of the entity, so I cant see how it is related to my problem.

Hibernate unable to delete child record

I'm unable to delete a child record while I'm updating (not deleting) the parent record. Also, I've read other posts, but it seems most of the others are using annotations rather than xml, so it can be difficult to see how they relate to my issue.
I have two tables: The EventInfo table that holds information about events and then the EventLicenseType table that only has two columns and both of those columns make up the primary key; one of the columns in the EventLicenseType table is a foreign key to the EventInfo table.
The problem is I can't seem to delete an EventLicenseType record. I've tried a bunch of different things and nothing is working for me. It seems like hibernate wants to put null as the eventinfoId column, which of course doesn't work. I have tried clearing out the Set and then doing the merge, and also specifically calling session.delete(eventlicenseTypeRec) and then doing the merge. Neither is working for me.
EventInfo.hbm.xml file:
<hibernate-mapping default-lazy="true">
<class name="Eventinfo" table="PA_EVENTINFO">
<id name="eventInfoId" type="int"
column="PA_EVENTINFOID">
<generator class="native" />
</id>
<property name="eventTypeId" type="java.lang.String"
column="PA_EVENTTYPEID" length="255" />
...Other columns not shown here for brevity...
<set name="eventLicenceTypeIds" lazy="false" cascade="all-delete-orphan">
<key column="PA_EVENTINFOID"/>
<one-to-many class="EventLicenseType" />
</set>
</class>
EventLicenseType.hbm.xml file:
<hibernate-mapping default-lazy="true">
<class name="EventLicenseType" table="PA_EVENTLICENSETYPE">
<composite-id>
<key-property name="licenseTypeId" type="java.lang.Integer" column="PA_LICENSETYPE"/>
<key-property name="eventInfoId" type="java.lang.Integer" column="PA_EVENTINFOID"/>
</composite-id>
</class>
Here is the EventInfo class. Again, there are more fields in the actual file, this is just the important pieces:
public class Eventinfo implements Serializable {
/** identifier field */
private int eventInfoId;
/** nullable persistent field */
#Field(name="eventInfo_eventTypeId")
private String eventTypeId;
#IndexedEmbedded
private Set<EventLicenseType> eventLicenceTypeIds;
/** default constructor */
public Eventinfo() {}
public int getEventInfoId() {
return this.eventInfoId;
}
public void setEventInfoId(int eventInfoId) {
this.eventInfoId = eventInfoId;
}
public String getEventTypeId() {
return this.eventTypeId;
}
public void setEventTypeId(String eventTypeId) {
this.eventTypeId = eventTypeId;
}
public Set<EventLicenseType> getEventLicenceTypeIds() {
return eventLicenceTypeIds;
}
public void setEventLicenceTypeIds(Set<EventLicenseType> eventLicenceTypeIds) {
this.eventLicenceTypeIds = eventLicenceTypeIds;
}
Here is the EventLicenseType class
public class EventLicenseType implements Serializable{
#Field
private int licenseTypeId;
private int eventInfoId;
public int getLicenseTypeId() {
return licenseTypeId;
}
public void setLicenseTypeId(int licenseTypeId) {
this.licenseTypeId = licenseTypeId;
}
public int getEventInfoId() {
return eventInfoId;
}
public void setEventInfoId(int eventInfoId) {
this.eventInfoId = eventInfoId;
}
}
Here is the method I'm executing in my DAO. For now there is only one record associated to the eventInfo record, so I'm just trying to see if I can delete that one. (Also note that eventinfo is defined in the method that surrounds this one).
public Eventinfo execute(Session session) throws Exception {
//Get the existing eventInfo record
Eventinfo existing = (Eventinfo)session.get(Eventinfo.class, eventinfo.getEventInfoId());
Iterator iter = existing.getEventLicenceTypeIds().iterator();
if (iter.hasNext()) {
EventLicenseType license = (EventLicenseType) iter.next();
iter.remove();
session.delete(license);
}
session.flush();
return (Eventinfo) session.merge(eventinfo);
}
On the above session.flush() line, I get an error: java.sql.BatchUpdateException: Cannot insert the value NULL into column 'PA_EVENTINFOID', table 'PA_EVENTLICENSETYPE'; column does not allow nulls. UPDATE fails. It shows that hibernate is trying to do:
update PA_EVENTLICENSETYPE set PA_EVENTINFOID=null where PA_EVENTINFOID=?
Why can't it just delete the record? Why is it trying to do an update??
I also tried changing the code to the below and get the same error.
public Eventinfo execute(Session session) throws Exception {
//Clear out the list
eventinfo.getEventLicenceTypeIds().clear();
return (Eventinfo) session.merge(eventinfo);
}
Can anyone help me with what I'm missing, or point me in the right direction?
You need to see whether the mapping between two tables are unilateral or bilateral. Basically you need to think or the rows of two tables as objects and cut all the ties between the objects of EventInfo and EventLicenceType tables. So if the relation is only from EventInfo -> EventLicenseType, you need to set the value of the EventLicenseType set to null in EventInfo object. Also if there is a mapping from EventLicenseType, you need to set the value of joining column to null. Then merge() the EventInfo object.
Noneed to explicitly delete or remove the EventLicenseType object. If here are no references to EventLicenseType object, the JVM will collect it as garbage.
Hope that solves your problem.

Hibernate: Mixing xml and annotation-based mapping for one class

I have to modify (better say enhance) an already existing entity class with some metadata. The calss's metadata is defined via annotations. But now I'd like to add some UserType definitions (hibernate-specific) to the entity class. It works well if I repeat all the metadata set via annotation in my hibernate-mapping-file but is there a way to only add my specific aspect without overwriting the annotation based stuff? Thanks in advance.
Oh, sorry. Of course I can:
#Entity
public class Thing implements Serializable {
#Id
private ContractNumber contractNumber;
#Column
private String columnViaAnnotation;
public ContractNumber getContractNumber() {
return contractNumber;
}
public void setContractNumber(ContractNumber contractNumber) {
this.contractNumber = contractNumber;
}
public String getColumnViaAnnotation() {
return columnViaAnnotation;
}
public void setColumnViaAnnotation(String columnViaAnnotation) {
this.columnViaAnnotation = columnViaAnnotation;
}
}
My mapping:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-mapping>
<class name="com.mypackage.Thing">
<id name="contractNumber" type="myType" column="contractNumber" />
</class>
<typedef class="com.mypackage.HibernateContractNumberConverter" name="myType" />
</hibernate-mapping>
The custom type works perfectly well. The datatype ContractNumber will be translated into a int and vice versa. But unfortunately the column 'columnViaAnnotation' disappears due to it's not mentioned in my mapping file. I was hoping that Hibernate is smart enough to merge both configurations (xml and annotation based). Is there any chance of doing this?

JPA - Increment a numeric field through a sequence programmatically

I have a JPA 2 web application (Struts 2, Hibernate 4 as JPA implementation only).
The current requirement is to add a (non-id) numeric sequential field, filled for certain rows only, to an existing entity. When inserting a new row, based on a certain condition, I need to set the new field to its highest value + 1 or to NULL.
For example:
ID NEW_FIELD DESCRIPTION
--------------------------------
1 1 bla bla
2 bla bla <--- unmatched: not needed here
3 bla bla <--- unmatched: not needed here
4 2 bla bla
5 3 bla bla
6 4 bla bla
7 bla bla <--- unmatched: not needed here
8 5 bla bla
9 bla bla <--- unmatched: not needed here
10 6 bla bla
In the good old SQL, it would be something like:
INSERT INTO myTable (
id,
new_field,
description
) VALUES (
myIdSequence.nextVal,
(CASE myCondition
WHEN true
THEN myNewFieldSequence.nextVal
ELSE NULL
END),
'Lorem Ipsum and so on....'
)
But I've no clue on how to achieve it with JPA 2.
I know I can define callbacks methods, but JSR-000317 Persistence Specification for Eval 2.0 Eval discourages some specific operations from inside it:
3.5 Entity Listeners and Callback Methods
- Lifecycle callbacks can invoke JNDI, JDBC, JMS, and enterprise beans.
- In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity
instances, or modify relationships within the same persistence
context.[43] A lifecycle callback method may modify the
non-relationship state of the entity on which it is invoked.
[43] The semantics of such operations may be standardized
in a future release of this specification.
Summarizing, yes to JDBC (!) and EJB, no to EntityManager and other Entities.
EDIT
I'm trying to achieve the solution described in the answer from #anttix, but I'm encoutering some problem, so please correct me where I'm wrong.
Table
MyTable
-------------------------
ID number (PK)
NEW_FIELD number
DESCRIPTION text
Main Entity
#Entity
#Table(name="MyTable")
public class MyEntity implements Serializable {
#Id
#SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
private Long id;
#OneToOne(cascade= CascadeType.PERSIST)
private FooSequence newField;
private String description
/* Getters and Setters */
}
Sub entity
#Entity
public class FooSequence {
#Id
#SequenceGenerator(name="seq_foo", sequenceName="seq_foo", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_foo")
private Long value;
/* Getter and Setter */
}
DAO
myEntity.setNewField(new FooSequence());
entityManager.persist(myEntity);
Exception
Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
[...]
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.hibernate.exception.SQLGrammarException: ERROR: relation "new_field" does not exist
[...]
Caused by: org.postgresql.util.PSQLException: ERROR: relation "new_field" does not exist
What am I doing wrong ? I'm pretty new to JPA 2 and I've never used an entity not associated to a physical table... this approach is totally new to me.
I guess I need to put the #Column definition somewhere: how could JPA possibly know that the newField column (mapped through ImprovedNamingStrategy to new_field on the database) is retrieved through the value property of the FooSequence entity ?
Some pieces of the puzzle are missing.
EDIT
As asked in comments, this is the persistence.xml:
<?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="MyService" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="hibernate.ejb.naming_strategy"
value="org.hibernate.cfg.ImprovedNamingStrategy"/>
<property name="hibernate.query.substitutions"
value="true 'Y', false 'N'"/>
<property name="hibernate.show_sql" value="true" />
<property name="format_sql" value="true" />
<property name="use_sql_comments" value="true" />
</properties>
</persistence-unit>
</persistence>
One possible solution is to use a separate entity with its own table that will encapsulate only the new field and have an OneToOne mapping with that entity. You will then instantiate the new entity only when you encounter an object that needs the additional sequence number. You can then use any generator strategy to populate it.
#Entity
public class FooSequence {
#Id
#GeneratedValue(...)
private Long value;
}
#Entity
public class Whatever {
#OneToOne(...)
private FooSequnce newColumn;
}
See:
Hibernate JPA Sequence (non-Id)
https://forum.hibernate.org/viewtopic.php?p=2405140
A gradle 1.11 runnable SSCCE (using Spring Boot):
src/main/java/JpaMultikeyDemo.java
import java.util.List;
import javax.persistence.*;
import lombok.Data;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
#Configuration
#EnableTransactionManagement
#EnableAutoConfiguration
public class JpaMultikeyDemo {
#Entity #Data
public static class FooSequence {
#Id #GeneratedValue private Long value;
}
#Entity #Data
public static class FooEntity {
#Id #GeneratedValue private Long id;
#OneToOne private FooSequence sequence;
}
#PersistenceContext
EntityManager em;
#Transactional
public void runInserts() {
// Create ten objects, half with a sequence value
for(int i = 0; i < 10; i++) {
FooEntity e1 = new FooEntity();
if(i % 2 == 0) {
FooSequence s1 = new FooSequence();
em.persist(s1);
e1.setSequence(s1);
}
em.persist(e1);
}
}
public void showAll() {
String q = "SELECT e FROM JpaMultikeyDemo$FooEntity e";
for(FooEntity e: em.createQuery(q, FooEntity.class).getResultList())
System.out.println(e);
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(JpaMultikeyDemo.class);
context.getBean(JpaMultikeyDemo.class).runInserts();
context.getBean(JpaMultikeyDemo.class).showAll();
context.close();
}
}
build.gradle
apply plugin: 'java'
defaultTasks 'execute'
repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-milestone" }
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-data-jpa:1.0.0.RC5"
compile "org.projectlombok:lombok:1.12.6"
compile "com.h2database:h2:1.3.175"
}
task execute(type:JavaExec) {
main = "JpaMultikeyDemo"
classpath = sourceSets.main.runtimeClasspath
}
See also: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-configure-datasource
This looks like it could be a good case for some AOP. First start by creating a custom field annotation #CustomSequenceGeneratedValue, and then annotate the field on the entity with it:
public class MyEntity {
...
#CustomSequenceGeneratedValue
private Long generatedValue;
public void setGeneratedValue(long generatedValue) {
}
}
Then an aspect is created to increment generated values:
#Aspect
public class CustomSequenceGeneratedValueAspect {
#PersistenceContext
private EntityManager em;
#Before("execution(* com.yourpackage.dao.SomeDao.*.*(..))")
public void beforeSaving(JoinPoint jp) throws Throwable {
Object[] args = jp.getArgs();
MethodSignature ms = (MethodSignature) jp.getSignature();
Method m = ms.getMethod();
Annotation[][] parameterAnnotations = m.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
Annotation[] annotations = parameterAnnotations[i];
for (Annotation annotation : annotations) {
if (annotation.annotationType() == CustomSequenceGeneratedEntity.class) {
... find generated properties run query and call setter ...
... Query query = em.createNativeQuery("select MY_SEQUENCE.NEXTVAL from dual");
}
}
}
}
}
Then the aspect is scanned with <aop:aspectj-autoproxy />, and applied to any Spring DAO saving entities of this type. The aspect would populate the sequence generated values based on a sequence, in a transparent way for the user.
You mentioned being open to using JDBC. Here is how you can you use Entity Callback with JdbcTemplate, the example uses Postgres's syntax for selecting next value in a sequence, just update it to use the right syntax for your DB.
Add this to your entity class:
#javax.persistence.EntityListeners(com.example.MyEntityListener.class)
And here is listener implementation (#Qualifier and required = true are necessary for it to work):
package com.example;
import javax.persistence.PostPersist;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
#Component
public class MyEntityListener {
private static JdbcTemplate jdbcTemplate;
#Autowired(required = true)
#Qualifier("jdbcTemplate")
public void setJdbcTemplate(JdbcTemplate bean) {
jdbcTemplate = bean;
}
#PostPersist
#Transactional
public void postPersis(MyEntity entity) {
if(isUpdateNeeded(entity)) {
entity.setMyField(jdbcTemplate.queryForObject("select nextval('not_hibernate_sequence')", Long.class));
}
}
private boolean isUpdateNeeded(MyEntity entity) {
// TODO - implement logic to determine whether to do an update
return false;
}
}
The hacky solution I used to keep it simple is the following:
MyEntity myEntity = new MyEntity();
myEntity.setDescription("blabla");
em.persist(myEntity);
em.flush(myEntity);
myEntity.setNewField(getFooSequence());
The complete code ("pseudo-code", I've written it directly on SO so it could have typos) with transaction handling would be like :
Entity
#Entity
#Table(name="MyTable")
public class MyEntity implements Serializable {
#Id
#SequenceGenerator(name="seq_id", sequenceName="seq_id", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_id")
private Long id;
private Long newField; // the optional sequence
private String description
/* Getters and Setters */
}
Main EJB:
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER) // default
public class MainEjb implements MainEjbLocalInterface {
#Inject
DaoEjbLocalInterface dao;
// Create new session, no OSIV here
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Long insertMyEntity(boolean myCondition) throws Exception {
try {
MyEntity myEntity = dao.insertMyEntity();
// if this break, no FooSequence will be generated
doOtherStuff();
// Do other non-database stuff that can break here.
// If they break, no FooSequence will be generated,
// and no myEntity will be persisted.
if (myCondition) {
myEntity.setNewField(dao.getFooSequence());
// This can't break (it would have break before).
// But even if it breaks, no FooSequence will be generated,
// and no myEntity will be persisted.
}
} catch (Exception e){
getContext().setRollbackOnly();
log.error(e.getMessage(),e);
throw new MyException(e);
}
}
}
DAO EJB
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER) // default
public class DaoEjb implements DaoEjbLocalInterface {
#PersistenceContext( unitName="myPersistenceUnit")
EntityManager em;
// default, use caller (MainEJB) session
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public MyEntity insertMyEntity() throws Exception{
MyEntity myEntity = new MyEntity();
myEntity.setDescription("blabla");
em.persist(myEntity);
em.flush(); // here it will break in case of database errors,
// eg. description value too long for the column.
// Not yet committed, but already "tested".
return myEntity;
}
// default, use caller (MainEJB) session
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public Long getFooSequence() throws Exception {
Query query = em.createNativeQuery("SELECT nextval('seq_foo')");
return ((BigInteger) query.getResultList().get(0)).longValue();
}
}
This will guarantee there will be no gaps in the FooSequence generation.
The only drawback, that I don't care at all in my use case, is that FooSequence and the #Id sequence are not synchronized, so two concurrent inserts may have "inverted" FooSequence values, respecto to their order of arrive, eg.
ID NEW FIELD
-------------
1 2
2 1

EJB3 CMP + programmatic configuration of Hibernate AnnotationConfiguration

I use EJB3 container managed persistence i.e an EntityManager is injected via #PersistenceContext annotation. The persistent context then may be propagated to nested EJBs. Transactions are also managed by the contaner (glassfish).
Usually I would drop persistence.xml into META-INF directory and the container would work out which provider to use and how to configure the EntityManagerFactory (based in hibernate specific properties).
My problem is that I need to hook into the EntityManagerFactory configuration process.
Particularly I need to change discriminator values in some PersistentClasses before the EntityManagerFactory gets configure'ed (frozen for any change).
This is how I do it with Spring, but need to do similar with pure EJB3 CMP (or may be with the help of Spring).
public class AnnotationSessionFactoryBean extends org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean {
/** Log4j logging instance. */
protected static Logger log = Logger.getLogger(AnnotationSessionFactoryBean.class);
//some data preloaded from the database using jdbc
private Map<String, DatabaseConfiguration> configs;
#Override
protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException {
//Load and process dynamic Mappings.
Iterator classMappingsIter = config.getClassMappings();
while(classMappingsIter.hasNext()) {
PersistentClass persistentClass = (PersistentClass) classMappingsIter.next();
String discriminatorValue = persistentClass.getDiscriminatorValue();
if(discriminatorValue != null) {
log.debug("DiscriminatorValue before [" + discriminatorValue + "]");
//here I replace discriminator values.
//The Discriminator values are coded in the annotations
//as names (words). These words need to be replaced with ids
//previously loaded from the database using jdbc.
//The names are constant in all environments, however the ids are
//are different.
discriminatorValue = StringUtil.replacePlaceholders(discriminatorValue, configs);
persistentClass.setDiscriminatorValue(discriminatorValue);
log.debug("DiscriminatorValue after [" + discriminatorValue + "]");
}
}
super.postProcessAnnotationConfiguration(config);
}
/**
* #return the configs
*/
public Map<String, DatabaseConfiguration> getConfigs() {
return configs;
}
/**
* #param configs the configs to set
*/
public void setConfigs(Map<String, DatabaseConfiguration> configs) {
this.configs = configs;
}
}
Thanks in advance,
Anton
I think I have found the solution.
The class org.hibernate.ejb.HibernatePersistence can be overridden.
public class HibernatePersistenceCustom extends org.hibernate.ejb.HibernatePersistence {
/** Log4j logging instance. */
protected static Logger log = Logger.getLogger(HibernatePersistenceCustom.class);
#Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
Ejb3Configuration cfg = new Ejb3Configuration();
//here you can configure it
doCustomConfiguration(cfg);
Ejb3Configuration configured = cfg.configure(info, map);
return configured != null ? configured.buildEntityManagerFactory() : null;
}
...
//other methods can also be overridden if required.
public void doCustomConfiguration(Ejb3Configuration config) {
//Load and process dynamic Mappings.
Iterator classMappingsIter = config.getClassMappings();
while(classMappingsIter.hasNext()) {
PersistentClass persistentClass = (PersistentClass) classMappingsIter.next();
String discriminatorValue = persistentClass.getDiscriminatorValue();
if(discriminatorValue != null) {
log.debug("DiscriminatorValue before [" + discriminatorValue + "]");
//here I replace discriminator values.
//The Discriminator values are coded in the annotations
//as names (words). These words need to be replaced with ids
//previously loaded from the database using jdbc.
//The names are constant in all environments, however the ids are
//are different.
discriminatorValue = StringUtil.replacePlaceholders(discriminatorValue, configs);
persistentClass.setDiscriminatorValue(discriminatorValue);
log.debug("DiscriminatorValue after [" + discriminatorValue + "]");
}
}
}
}
then in persistence.xml instead of org.hibernate.ejb.HibernatePersistence put com.mydomain.persistence.HibernatePersistenceCustom
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.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_1_0.xsd">
<persistence-unit name="mypersistenceunit" transaction-type="JTA">
<provider>com.mydomain.persistence.HibernatePersistenceCustom</provider>
<jta-data-source>jdbc/mydatasource</jta-data-source>
<properties>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.format_sql" value="false"/>
<property name="hibernate.use_sql_comments" value="false"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
</properties>
</persistence-unit>
</persistence>
Have not tested it yet, but I think it will work.
Thanks
You could override metadata annotations by providing an XML mapping file (see the Chapter 10 XML Descriptor in the JPA 1.0 specification).
Of course, this is not dynamic (unless you generate the XML mapping file using for example FreeMarker and feed values from the database).

Categories

Resources