I recently wrote a class, in my web application, for parsing a huge XML file and feed a db table with its content. My application is running on Wildfly9, and uses JPA with Hibernate provider to handle a MySQL DB.
The AS configuration is pretty standard, I just added my datasource conf:
<datasource jta="false" jndi-name="java:jboss/datasources/spazio_visione" pool-name="spazio_visione" enabled="true" use-ccm="false">
<connection-url>jdbc:mysql://127.0.0.1:3306/spazio_visione?zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver>mysql</driver>
<security>
<user-name>myuser</user-name>
<password>mypasswd</password>
</security>
<validation>
<validate-on-match>false</validate-on-match>
<background-validation>false</background-validation>
</validation>
<statement>
<share-prepared-statements>false</share-prepared-statements>
</statement>
</datasource>
And here's 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="backoffice" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/spazio_visione</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.jdbc.batch_size" value="100" />
<property name="hibernate.order_inserts" value="true" />
<property name="hibernate.order_updates" value="true" />
<!-- <property name="hibernate.show_sql" value="true"/> -->
<!-- <property name="hibernate.hbm2ddl.auto" value="validate"/> -->
</properties>
</persistence-unit>
</persistence>
Everything has always worked fine, using JPA entities to manage my domain model.
Back to my parser... actually, for many reasons, it needs to use native JDBC queries to insert my data in the db. Here's the code:
public class XMLFeedParser extends DefaultHandler {
#Inject Logger logger;
#Resource(lookup="java:jboss/datasources/spazio_visione") DataSource datasource;
private static final int STATEMENT_BATCH_THRESHOLD = 1000;
private MyXMLFeedItem item;
private Connection connection;
private PreparedStatement ps;
public XMLFeedParser() {
}
protected void initParser() throws SQLException {
connection = datasource.getConnection();
Statement deleteStatement = connection.createStatement();
deleteStatement.executeUpdate("DELETE FROM mytable WHERE id_feed = "+feed.getId());
deleteStatement.close();
ps = connection.prepareStatement(
"INSERT INTO mytable "
+ "( first, second, ...) "
+ "values ( ?, ?, ... )"
);
}
protected void finalizeParser() throws SQLException {
if (ps!=null) {
ps.executeBatch();
ps.close();
}
if (connection!=null) {
connection.close();
}
}
public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException {
File file = Paths.get(filePath).toFile();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
initParser();
saxParser.parse(file, this);
finalizeParser();
}
private void writeToDb(MyXMLFeedItem item) {
try {
ps.setString(1, "first");
ps.setString(2, "second");
// ...
ps.addBatch();
if ( counter % STATEMENT_BATCH_THRESHOLD == 0 ) {
ps.executeBatch();
}
} catch (SQLException e) {
logger.severe(e.getMessage());
}
}
#Override
public void startElement(String namespaceURI, String localName, String qualifiedName, Attributes attrs) throws SAXException {
// ...parsing logic
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
// ...parsing logic
}
#Override
public void endElement(String namespaceURI, String localName, String qualifiedName) throws SAXException {
// calls writeToDb() for each record found
}
}
My XMLFeedParser is injected (using #Inject) in one of my EJBs, which will call parseAndWriteToDatabase() method. It works!
The pain starts here. Since the end of the parsing, my application begins giving errors in other points, just randomly. The stacktrace looks like this:
Caused by: javax.resource.ResourceException: IJ000453: Unable to get managed connection for java:jboss/datasources/spazio_visione
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:646)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:552)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.allocateConnection(AbstractConnectionManager.java:737)
at org.jboss.jca.adapters.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:138)
... 165 more
Caused by: javax.resource.ResourceException: IJ000655: No managed connections available within configured blocking timeout (30000 [ms])
at org.jboss.jca.core.connectionmanager.pool.mcp.SemaphoreArrayListManagedConnectionPool.getConnection(SemaphoreArrayListManagedConnectionPool.java:553)
at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getSimpleConnection(AbstractPool.java:622)
at org.jboss.jca.core.connectionmanager.pool.AbstractPool.getConnection(AbstractPool.java:594)
at org.jboss.jca.core.connectionmanager.AbstractConnectionManager.getManagedConnection(AbstractConnectionManager.java:579)
... 168 more
It looks like I didn't close the connection, but that's not true!
Any suggestions?
There are a few possibilities what could go wrong. First, you are opening the connection in initParser(), but closing it in finalizeParser(), without using finally. If an exception is thrown, the connection is not closed. It would still be better to use try-with-resources.
Another potential problem is that the class is not thread-safe. For example, if an instance is used without synchronization, if you call XMLFeedParser.initParser() two times before finalizeParser(), you may lose your reference to connection which is then never closed (how does your EJB which injects the XMLFeedParser look like?)
edit: using try-with-resources:
it depends where you need your Connection. you could open the connection in parseAndWriteToDatabase() and pass it to the methods in which you need it. So you don't have to explicitly call close(). Also your PreparedStatements and ResultSets could be wrapped in try-with-resources.
for example something like:
public void parseAndWriteToDatabase(String filePath) throws ParserConfigurationException, SAXException, IOException, SQLException {
// ...
try (Connection connection = getDataSource().getConnection();)
{
initParser(connection);
saxParser.parse(file, this);
finalizeParser(connection);
}
}
So when your Connection and other variables are not members of the class, you don't have to worry about other threads accessing them.
Related
I have a mySQL database for manage user information, and I am using JTA datasource for my mySQL database, here is what the persistence.xml look like:
<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_2_0.xsd"
version="2.0">
<persistence-unit name="SensorCloudPU" transaction-type="JTA">
<jta-data-source>java:/SensorCloudPU</jta-data-source>
<!-- <non-jta-data-source>java:/SensorCloudPU</non-jta-data-source> -->
<class>com.sensorhound.common.domain.impl.AnomalousInfo</class>
<class>com.sensorhound.common.domain.impl.Code</class>
<class>com.sensorhound.common.domain.impl.Device</class>
<class>com.sensorhound.common.domain.impl.Executable</class>
<class>com.sensorhound.common.domain.impl.Group</class>
<class>com.sensorhound.common.domain.impl.GroupAlert</class>
<class>com.sensorhound.common.domain.impl.GroupRule</class>
<class>com.sensorhound.common.domain.impl.GroupRuleDefinition</class>
<class>com.sensorhound.common.domain.impl.GroupRuleStatus</class>
<class>com.sensorhound.common.domain.impl.Node</class>
<class>com.sensorhound.common.domain.impl.NodeAlert</class>
<class>com.sensorhound.common.domain.impl.NodeRule</class>
<class>com.sensorhound.common.domain.impl.NodeRuleDefinition</class>
<class>com.sensorhound.common.domain.impl.Organization</class>
<class>com.sensorhound.common.domain.impl.PastGroupStatus</class>
<class>com.sensorhound.common.domain.impl.Trace</class>
<class>com.sensorhound.common.domain.impl.TrainingSession</class>
<class>com.sensorhound.common.domain.impl.User</class>
<class>com.sensorhound.common.domain.impl.Role</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value = "false" />
<property name="hibernate.connection.autocommit" value="true" />
<property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
<property name="transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
<property name="jta.UserTransaction" value="java:jboss/UserTransaction"/>
</properties>
</persistence-unit>
</persistence>
And I have an endpoint like this:
#Path("/delete")
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public Response deleteUser(#FormParam("organization_id") Integer organizationId,
#FormParam("username") String username) throws JsonProcessingException, NotSupportedException,
SystemException, SecurityException, IllegalStateException, RollbackException,
HeuristicMixedException, HeuristicRollbackException, NamingException {
Organization org = organizationDAO.getByOrganizationId(organizationId);
userDao.deleteUserByUserNameAndOrganization(username, org);
return Response.status(Response.Status.OK).build();
}
And the DAO is like this:
public class userDAO {
#PersistenceContext(unitName = "SensorCloudPU")
protected EntityManager em;
#Resource
protected UserTransaction utx;
public void deleteUserByUserNameAndOrganization(String userName, Organization org)
throws NotSupportedException, SystemException, SecurityException, IllegalStateException,
RollbackException, HeuristicMixedException, HeuristicRollbackException {
Query q = this.em.createNamedQuery(User.Q_GET_BY_USERNAME_AND_ORGANIZATION);
q.setParameter("organization", org);
q.setParameter("user_name", userName);
User u = this.executeQueryForSingleResult(q);
if (u == null) {
return;
}
utx.begin();
this.em.remove(u);
utx.commit();
}
}
But every time when I load the page and try to delete the from the database, I got this error:
Resource lookup for injection failed: java:jboss/UserTransaction]
UserTransaction [Root exception is java.lang.IllegalStateException: WFLYEJB0137: Only session and message-driven beans with bean-managed transaction demarcation are allowed to access UserTransaction]
You can't use UserTransaction in an EJB, unless you add #TransactionManagement(BEAN)
What TransactionManagement does is
Specifies whether a session bean or message driven bean has container managed transactions or bean managed transactions. If this annotation is not used, the bean is assumed to have container-managed transaction management.
#TransactionManagement(BEAN)
public class userDAO {
Since you are primarily concerned with transaction management I suggest that you convert your DAO into an EJB. It only takes one line:
#Stateless
public class userDAO {
#PersistenceContext(unitName = "SensorCloudPU")
protected EntityManager em;
public void deleteUserByUserNameAndOrganization(String userName, Organization org) {
Query q = this.em.createNamedQuery(User.Q_GET_BY_USERNAME_AND_ORGANIZATION);
q.setParameter("organization", org);
q.setParameter("user_name", userName);
User u = this.executeQueryForSingleResult(q);
if (u != null) {
this.em.remove(u);
}
}
}
and you can see that it simplifies things significantly. EJBs give you JTA transaction demarcation (and rollback if needed) for free.
This will work even if you are building a WAR only deployment.
You could also add #Stateless to your JAX-RS endpoint if you want. At the very least you will get a bit more monitoring than you may otherwise have.
Image
ERROR:
1) AdminModel.java - Model class.
2) HibernateUtil.java facilitates the Hibernate DB conn.
3) AdminDAO.java - u guyz know what these are...I'll save the pain to explain...and oh yes...m already through days of pain with this bug ...trynna debug...i've got deadlines to meet... if u guyz could help me it'd be a matter of great deal...
public class AdminModel {
private int adminID;
private String username;
private String password;
public int getAdminID() {
return adminID;
}
public void setAdminID(int adminID) {
this.adminID = adminID;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
HibernateUtil.java
public class HibernateUtil {
public static SessionFactory sessionFactory;
static {
try {
/* File f=new File("O:/#workspace/#eclipse/ekatabookstore.com/src/hibernate.cfg.xml");
sessionFactory =new Configuration().configure(f).buildSessionFactory();
*/
/****OR****/
String hibernatePropsFilePath = "O:/#workspace/#eclipse/ekatabookstore.com/src/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
Configuration configuration = new Configuration();
configuration.configure(hibernatePropsFile);
configuration.addResource("ekatabookstore.hbm.xml");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (Exception ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
//throw new ExceptionInInitializerError(ex);
}
}
public static Session openSession() {
return HibernateUtil.sessionFactory.openSession();
//return sessionFactory.getCurrentSession();
}
}
AdminDAO.java
public AdminDAO(AdminModel adminUserObj) {
public void createAdmin() {
/*
* CRUD operation of HIBERNATE C-->Create SessionFactory Object is a
* heavy object and takes up huge resources, so it is better to create
* only one object and share it where needed.
*/
SessionFactory sessionFactoryObj = HibernateUtil.sessionFactory;
// System.out.println(sessionFactoryObj.getClass().getName());
Session session = sessionFactoryObj.openSession();
session.beginTransaction();// Transaction Started
session.save(adminObj);// SAVED
session.getTransaction().commit();// Transaction Ended
System.out.println("!!!SUCCESSFUL CREATE!!!");
session.close();// CLOSE session resource of Hibernate
Notification.notificationMsg = "ADMIN CREATE - SUCCESSFUL!";
}
}
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!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://localhost:3306/ekatabookstoreDB</property>
<property name="connection.username">xyz</property>
<property name="connection.password">xyz</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<!-- <property name="hbm2ddl.auto">create</property> -->
<property name="hbm2ddl.auto">update</property>
<mapping resource="ekatabookstore.hbm.xml" />
</session-factory>
</hibernate-configuration>
hbn.properties
hiberNateCfgFileName=hibernate.cfg.xml
ekatabookstore.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.ekatabookstore.layer.service.model.AdminModel" table="admin">
<id name="adminID" type="integer" column="id_admin">
<generator class="assigned" />
</id>
<property name="username" type="string" column="username" not-null="true" />
<property name="password" type="string" column="password" not-null="true" />
</class>
</hibernate-mapping>
If you are using spring then, Add autowire on sessionfactory or get it from application context. You can use following code
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextProvider implements ApplicationContextAware{
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
}
You can use this class anywhere in your code like
ApplicationContextProvider.getApplicationContext().getBean("sessionFactory");
Please try the following code for creating sessionfactory
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure() // configures settings from hibernate.cfg.xml
.build();
try {
sessionFactory = new MetadataSources( registry).buildMetadata().buildSessionFactory();
}
catch (Exception e) {
StandardServiceRegistryBuilder.destroy( registry );
}
Hope this helps.
I'm developing a Tomcat-based webapp I'm trying to get to talk to an existing AS400 data store. I've copied most of the settings from an existing web app which works, but when I run my new app I get this:
2013-08-08 13:50:11,988 ERROR [RMI TCP Connection(3)-127.0.0.1] org.hibernate.tool.hbm2ddl.SchemaValidator - could not get database metadata
java.sql.SQLException: [SQL5016] Qualified object name SYSSEQUENCES not valid.
at com.ibm.as400.access.JDError.throwSQLException(JDError.java:646)
at com.ibm.as400.access.JDError.throwSQLException(JDError.java:617)
at com.ibm.as400.access.AS400JDBCStatement.commonPrepare(AS400JDBCStatement.java:1578)
at com.ibm.as400.access.AS400JDBCStatement.executeQuery(AS400JDBCStatement.java:2138)
at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
at org.apache.tomcat.dbcp.dbcp.DelegatingStatement.executeQuery(DelegatingStatement.java:208)
at org.hibernate.tool.hbm2ddl.DatabaseMetadata.initSequences(DatabaseMetadata.java:151)
at org.hibernate.tool.hbm2ddl.DatabaseMetadata.<init>(DatabaseMetadata.java:69)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:132)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:378)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:288)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310)
[SNIP]
Seems like it's failing looking for the SYSSEQUENCES object (presumably a table) but there's no such table in my schema, or anywhere that I'm aware of. Why is it doing this and how can I correct it?
Here's the SERVER.XML resource that I'm using to connect:
<Resource
name="jdbc/myresource"
auth="Container"
driverClassName="com.ibm.as400.access.AS400JDBCDriver"
maxActive="20"
maxIdle="10"
maxWait="5000"
password="mypassword"
testOnBorrow="true"
type="javax.sql.DataSource"
url="jdbc:as400://mysystem.mycompany.com;libraries=LIB1 LIB2 LIB3;dateformat=iso;timeformat=iso;prompt=false;naming=system;transaction isolation=none"
username="myusername"
validationQuery="SELECT * from sysibm/sysdummy1"/>
Here's my PERSISTENCE.XML:
<persistence-unit name="myPersistenceUnit">
<properties>
<property name="hibernate.generate_statistics" value="true" />
<property name="hibernate.cache.use_structured_entries" value="true" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.jdbc.batch_size" value="100" />
<property name="hibernate.dialect" value="org.hibernate.dialect.DB2400Dialect" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
</properties>
</persistence-unit>
Examining the stack trace the issue begins at org.hibernate.tool.hbm2ddl.DatabaseMetadata.initSequences.
The initSequences method checks the dialect for sequence support:
DatabaseMetadata.initSequences
private void initSequences(Connection connection, Dialect dialect) throws SQLException {
if ( dialect.supportsSequences() ) {
String sql = dialect.getQuerySequencesString();
DB2400Dialect reports that it does not:
DB2400Dialect.java
public class DB2400Dialect extends DB2Dialect {
#Override
public boolean supportsSequences() {
return false;
}
For reference the base DB2Dialect does support sequences and references sysibm.syssequences:
DB2Dialect.java
#Override
public boolean supportsSequences() {
return true;
}
#Override
public String getQuerySequencesString() {
return "select seqname from sysibm.syssequences";
}
It would appear that either your dialect is not being set properly or your version of DB2400Dialect is reporting that it does support sequences.
Try using the following:
public class DB2AS400Dialect extends DB2400Dialect {
#Override
public String getQuerySequencesString() {
return null;
}
}
Since the Dialect class does the following:
public SequenceInformationExtractor getSequenceInformationExtractor() {
if ( getQuerySequencesString() == null ) {
return SequenceInformationExtractorNoOpImpl.INSTANCE;
}
else {
return SequenceInformationExtractorLegacyImpl.INSTANCE;
}
}
Instead of consulting the boolean value if the dialect supports sequences.
I'm trying to create a test for a file that's using a jdbc resource. Regular tests work fine throughout the project, except for when I'm trying to test something that is using an entity manager. I think I'm missing something simple here and would appreciate any help.
Here's the error.
Feb 27, 2012 3:04:44 PM com.sun.enterprise.v3.server.ApplicationLifecycle deploy
SEVERE: javax.naming.NamingException: Lookup failed for 'jdbc/foundation' in SerialContext[myEnv={com.sun.enterprise.connectors.jndisuffix=__pm, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NameNotFoundException: foundation not found]
java.lang.RuntimeException: javax.naming.NamingException: Lookup failed for 'jdbc/foundation' in SerialContext[myEnv={com.sun.enterprise.connectors.jndisuffix=__pm, java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NameNotFoundException: foundation not found]
Here's some of the troubleshooting steps I've taken:
Added the glassfish extras to the maven POM
I have placed the persistence file in both the /main/resources and /test/resources folders.
I have tried adding properties to the context object in the #BeforeClass method in the DonationsFacadeTest file along with about a million other configurations similar to the one below.
Environment Info:
Using Netbeans 7.0.1
Maven Project
Glassfish 3.1
DonationsFacade.java - This class is using a facade pattern to provide indirect access to JPA entities.
#Stateless
public class DonationsFacade extends AbstractFacade<Donations> {
#PersistenceContext(unitName = "FoundationPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public DonationsFacade() {
super(Donations.class);
}
public boolean setPaid(int id, String transId)
{
try{
Donations don = em.find(Donations.class, id);
don.setStatus("Paid");
don.setConfirmationnumber(transId);
em.persist(don);
em.flush();
return true;
}
catch(Exception ex)
{
return false;
}
}
Test File - Just trying to write a simple test to to verify the data.
public class DonationsFacadeTest {
#Test
public void testSetPaid() throws Exception {
int id = 1;
String transId = "123";
EJBContainer ejbC = EJBContainer.createEJBContainer();
Context ctx = ejbC.getContext();
DonationsFacade instance = (DonationsFacade)ctx.lookup("java:global/classes/DonationsFacade");
boolean expResult = false;
boolean result = instance.setPaid(id, transId);
assertEquals(expResult, result);
ejbC.close();
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"...>
<persistence-unit name="FoundationPU" transaction-type="JTA">
<jta-data-source>jdbc/foundation</jta-data-source>
<properties/>
</persistence-unit>
</persistence>
glassfish-resources.xml
<resources>
<jdbc-resource enabled="true" jndi-name="jdbc/foundation" object-type="user" pool-name="mysql_foundation_rootPool">
<description/>
</jdbc-resource>
<jdbc-connection-pool allow-non-component-callers="false"
associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
connection-validation-method="table"
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
fail-all-connections="false" idle-timeout-in-seconds="300"
is-connection-validation-required="false"
is-isolation-level-guaranteed="true"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="false"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="connectionPool"
non-transactional-connections="false"
ping="false"
pool-resize-quantity="2"
pooling="true"
res-type="javax.sql.DataSource"
statement-cache-size="0"
statement-leak-reclaim="false"
statement-leak-timeout-in-seconds="0"
statement-timeout-in-seconds="-1"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"
wrap-jdbc-objects="true">
<property name="URL" value="jdbc:mysql://localhost:3306/foundation"/>
<property name="User" value="root"/>
<property name="Password" value="thepassword"/>
</jdbc-connection-pool>
</resources>
Again, thanks for the help, very much appreciated.
Figured out the answer here. I basically injected a entitymanager into the class. Hopefully this is helpful to someone who is trying to do this same thing.
public class DonationsFacadeTest {
private EntityManager em;
private EntityTransaction tx;
DonationsFacade donations;
public DonationsFacadeTest() {
}
#Before
public void setUpClass() throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Foundation2");
em = emf.createEntityManager();
tx = em.getTransaction();
this.donations = new DonationsFacade();
this.donations.em = em;
}
/**
* Test of setPaid method, of class DonationsFacade.
*/
#Test
public void testSetPaid() throws Exception {
int id = 1;
String transId = "123";
boolean expResult = false;
boolean result = donations.setPaid(id, transId);
assertEquals(expResult, result);
}
}
Modified the persistence.xml file appropriately.
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" ... >
<persistence-unit name="Foundation2" transaction-type="RESOURCE_LOCAL">
<class>org.scmc.foundation.session.DonationsFacadeTest</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/foundation"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="password"/>
</properties>
</persistence-unit>
And finally added the appropriate driver to the POM file.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
I'm trying to invoke method based on some interval time, here are some beans inside applicationContext.xml
<bean id="MngtTarget"
class="com.management.engine.Implementation"
abstract="false" lazy-init="true" autowire="default" dependency-check="default">
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="MngtTarget" />
<property name="targetMethod" value="findItemByPIdEndDate"/>
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<!-- 10 seconds -->
<property name="startDelay" value="10000" />
<!-- repeat every 50 seconds -->
<property name="repeatInterval" value="20000" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
Here is the method I'm trying to invoke :
public List<Long> I need findItemByPIdEndDate() throws Exception {
List<Long> list = null;
try{
Session session = sessionFactory.getCurrentSession();
Query query = session.getNamedQuery("endDateChecker");
list = query.list();
for(int i=0; i<list.size(); i++)
{
System.out.println(list.get(i));
}
System.out.println("Total " + list.size());
}catch (HibernateException e){
throw new DataAccessException(e.getMessage());
}
return list;
}
Here is the exception message that I get :
Invocation of method 'findItemByPIdEndDate' on target class [class com.management.engine.Implementation] failed; nested exception is No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I've spent time googling alot so far also I've tried to modify my method like this :
public List<Long> I need findItemByPIdEndDate() throws Exception {
List<Long> list = null;
try{
Session session = sessionFactory.openSession();
Query query = session.getNamedQuery("endDateChecker");
list = query.list();
for(int i=0; i<list.size(); i++)
{
System.out.println(list.get(i));
}
System.out.println("Total " + list.size());
session.close();
}catch (HibernateException e){
throw new DataAccessException(e.getMessage());
}
return list;
}
And I get different error msg, I get : Invocation of method 'findItemByPIdEndDate' on target class [class com.management.engine.Implementation] failed; nested exception is could not execute query] , anyone knows what is this all about, any suggestions ? thank you
Also my queries.hbm.xml
<hibernate-mapping>
<sql-query name="endDateChecker">
<return-scalar column="PId" type="java.lang.Long"/>
<![CDATA[select
item_pid as PId
from
item
where
end_date < trunc(sysdate)]]>
</sql-query>
</hibernate-mapping>
For the second error ("could not execute the query"), I don't know and I'm really wondering what the session looks like.
In deed, AFAIK, the persistent context is not available to Quartz Jobs as nothing take care of establishing a Hibernate Session for them (Quartz runs outside the context of Servlets and the open session in view pattern doesn't apply here). This is why you get the first error ("No hibernate session bound to thread").
One solution for this is described in AOP – Spring – Hibernate Sessions for background threads / jobs. In this post, the author shows how you can use Spring AOP proxies to wire a hibernate interceptor that gives you access to the persistence context and it takes cares of closing and opening the sessions for you.
Didn't test it myself though, but it should work.
I too was facing the same "HibernateException: No Hibernate Session bound to thread" exception
2012-01-13 13:16:15.005 DEBUG MyQuartzJob Caught an exception
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
at com.company.somemodule.dao.hibernate.AbstractHibernateDaoImpl.getSession(AbstractHibernateDaoImpl.java:107)
at com.company.somemodule.dao.hibernate.SomeDataDaoImpl.retrieveSomeData(SomeDataDaoImpl.java:264)
and I solved it by following the example here.
Relevant code
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import com.company.somemodule.dao.SomeDataDao;
import com.company.somemodule.SomeData;
public class MyQuartzJob extends QuartzJobBean implements Runnable {
private boolean existingTransaction;
private JobExecutionContext jobExecCtx;
private static Logger logger = LoggerFactory.getLogger(MyQuartzJob.class);
private SomeDataDao someDataDao; //set by Spring
private Session session;
private SessionFactory hibernateSessionFactory; //set by Spring
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException
this.jobExecCtx = ctx;
run();
}
private void handleHibernateTransactionIntricacies() {
session = SessionFactoryUtils.getSession(hibernateSessionFactory, true);
existingTransaction = SessionFactoryUtils.isSessionTransactional(session, hibernateSessionFactory);
if (existingTransaction) {
logger.debug("Found thread-bound Session for Quartz job");
} else {
TransactionSynchronizationManager.bindResource(hibernateSessionFactory, new SessionHolder(session));
}
}
private void releaseHibernateSessionConditionally() {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after TransactionalQuartzTask");
} else {
TransactionSynchronizationManager.unbindResource(hibernateSessionFactory);
SessionFactoryUtils.releaseSession(session, hibernateSessionFactory);
}
}
#Override
public void run() {
// ..
// Do the required to avoid HibernateException: No Hibernate Session bound to thread
handleHibernateTransactionIntricacies();
// Do the transactional operations
try {
// Do DAO related operations ..
} finally {
releaseHibernateSessionConditionally();
}
}
public void setHibernateSessionFactory(SessionFactory hibernateSessionFactory) {
this.hibernateSessionFactory = hibernateSessionFactory;
}
public void setSomeDataDao(SomeDataDao someDataDao ) {
this.someDataDao = someDataDao ;
}
}
Relevant bean configuration inside applicationContext.xml
<bean name="myJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.somecompany.worker.MyQuartzJob" />
<property name="jobDataAsMap">
<map>
<entry key="hibernateSessionFactory" value-ref="sessionFactory" />
<entry key="someDataDao" value-ref="someDataDao" />
</map>
</property>
</bean>
There's bug spring https://jira.spring.io/browse/SPR-9020
And there's workaround.
Configure session with hibernate.current_session_context_class property with this class:
https://gist.github.com/seykron/4770724