I'm little confused how transactions work in EJBs. I've always thought that all transaction aware objects in container managed EJBs are all committed or rollbacked when a method with TransactionAttribute=REQUIRED_NEW is finished but unfortunately it's not in my case. I don't have my code in front of me so I can't include whole example but what I ask for is just the confirmation of the idea of how it should work.
Only key points of my code just from the top of my head are presented:
EntityManager em; //injected
[...]
public void someEJBMethod() {
[...]
em.persist(someObject);
[...]
Session session = JpaHelper.getEntityManager(em).getActiveSession();
[...]
session.executeQuery(query, args);
[...]
if (someCondition) {
throw new EJBException();
}
[...]
}
And my problem is that when EJBException is thrown database changes caused by em.persist are rollbacked but changes caused by session.executeQuery are committed.
Is it expected behaviour?
I'm using Glassfish 3.1.2, EclipseLink 2.3.2 with Oracle database
Update (test case added)
I've created working test case to show the problem
First database objects:
create table txtest
(id number not null primary key,
name varchar2(50) not null);
create or replace function txtest_create(p_id number, p_name varchar2) return number is
begin
insert into txtest
(id, name)
values
(p_id, p_name);
return p_id;
end;
Definition of a database connection (from domain.xml)
<jdbc-connection-pool driver-classname="" datasource-classname="oracle.jdbc.pool.OracleConnectionPoolDataSource" res-type="javax.sql.ConnectionPoolDataSource" description="" name="TxTest">
<property name="User" value="rx"></property>
<property name="Password" value="rx"></property>
<property name="URL" value="jdbc:oracle:thin:#test:1529:test"></property>
</jdbc-connection-pool>
<jdbc-resource pool-name="TxTest" description="" jndi-name="jdbc/TxTest"></jdbc-resource>
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="txTest">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/TxTest</jta-data-source>
<class>txtest.TxTest</class>
</persistence-unit>
</persistence>
session bean:
#Stateless
public class TxTestBean implements TxTestBeanRemote, TxTestBeanLocal {
private static Logger log = Logger.getLogger(TxTestBean.class.getName());
#PersistenceContext(unitName="txTest")
EntityManager em;
#SuppressWarnings({ "unchecked", "rawtypes" })
#Override
public void txTest(boolean throwException) {
TxTest t = new TxTest();
t.setId(1L);
t.setName("em.persist");
em.persist(t);
Session session = JpaHelper.getEntityManager(em).getActiveSession();
log.info("session : " + String.valueOf(System.identityHashCode(session)));
PLSQLStoredFunctionCall call = new PLSQLStoredFunctionCall();
call.setProcedureName("txtest_create");
call.addNamedArgument("p_id", JDBCTypes.NUMERIC_TYPE);
call.addNamedArgument("p_name", JDBCTypes.VARCHAR_TYPE, 50);
call.setResult(JDBCTypes.NUMERIC_TYPE);
ValueReadQuery query = new ValueReadQuery();
query.setCall(call);
query.addArgument("p_id");
query.addArgument("p_name");
t = new TxTest();
t.setId(2L);
t.setName("session.executeQuery");
List args = new ArrayList();
args.add(t.getId());
args.add(t.getName());
Long result = ((Number)session.executeQuery(query, args)).longValue();
//added to see the state of txtest table in the database before exception is thrown
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("result=" + result.toString());
if (throwException) {
throw new EJBException("Test error #1");
}
}
}
entries from server.log when txTest(true) is invoked:
[#|2012-05-21T12:04:15.361+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|client acquired: 21069550|#]
[#|2012-05-21T12:04:15.362+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|TX binding to tx mgr, status=STATUS_ACTIVE|#]
[#|2012-05-21T12:04:15.362+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|acquire unit of work: 16022663|#]
[#|2012-05-21T12:04:15.362+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|persist() operation called on: txtest.TxTest#11b9605.|#]
[#|2012-05-21T12:04:15.363+0200|INFO|glassfish3.1.2|txtest.TxTestBean|_ThreadID=167;_ThreadName=Thread-2;|session : 16022663|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.query|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Execute query ValueReadQuery()|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Connection acquired from connection pool [read].|#]
[#|2012-05-21T12:04:15.364+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|reconnecting to external connection pool|#]
[#|2012-05-21T12:04:15.365+0200|FINE|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.sql|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|
DECLARE
p_id_TARGET NUMERIC := :1;
p_name_TARGET VARCHAR(50) := :2;
RESULT_TARGET NUMERIC;
BEGIN
RESULT_TARGET := txtest_create(p_id=>p_id_TARGET, p_name=>p_name_TARGET);
:3 := RESULT_TARGET;
END;
bind => [:1 => 2, :2 => session.executeQuery, RESULT => :3]|#]
[#|2012-05-21T12:04:15.370+0200|FINEST|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|Connection released to connection pool [read].|#]
[#|2012-05-21T12:04:35.372+0200|INFO|glassfish3.1.2|txtest.TxTestBean|_ThreadID=167;_ThreadName=Thread-2;|result=2|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|TX afterCompletion callback, status=ROLLEDBACK|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.transaction|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|release unit of work|#]
[#|2012-05-21T12:04:35.372+0200|FINER|glassfish3.1.2|org.eclipse.persistence.session.file://txTest/_txTest.connection|_ThreadID=167;_ThreadName=Thread-2;ClassName=null;MethodName=null;|client released|#]
[#|2012-05-21T12:04:35.373+0200|WARNING|glassfish3.1.2|javax.enterprise.system.container.ejb.com.sun.ejb.containers|_ThreadID=167;_ThreadName=Thread-2;|EJB5184:A system exception occurred during an invocation on EJB TxTestBean, method: public void txtest.TxTestBean.txTest(boolean)|#]
[#|2012-05-21T12:04:35.373+0200|WARNING|glassfish3.1.2|javax.enterprise.system.container.ejb.com.sun.ejb.containers|_ThreadID=167;_ThreadName=Thread-2;|javax.ejb.EJBException: Test error #1
What surprised me the most is that when I checked txtest table during this 20 sec. sleep the record (2,"session.executeQuery") was already there.
It seems like session.executeQuery somehow commits its work (but not the whole transaction).
Can someone explain this behaviour?
I'm not sure what JpaHelper.getEntityManager(em).getActiveSession(); is supposed to do exactly, but it seems likely this doesn't return a container managed entity manager. Depending on how it's exactly implemented, this may not participate in the ongoing (JTA) transaction.
Normally though, transactional resources all automatically participate in the ongoing JTA transaction. In broad lines they do this by checking if there's such on ongoing transaction, and if there indeed is, they register themselves with this transaction.
In EJB, REQUIRES_NEW is not the only mode that can start a transaction 'REQUIRES' (the default) also does this incase the client didn't start a transaction.
I've got it solved!!!
It turned out that EclipseLink uses read connection pools for processing read queries (and obviously that kind of pools uses autocommit or even don't use transactions at all) and default connection pools for data modification queries. So what I had to do was changing:
ValueReadQuery query = new ValueReadQuery();
into
DataModifyQuery query = new DataModifyQuery();
and it works like a charm.
Update
DataModifyQuery doesn't allow to get the result of the function. It returns the number of modified rows. So I got back to ValueReadQuery but used configuration parameter in persistance.xml
<property name="eclipselink.jdbc.exclusive-connection.mode" value="Always"/>
This parameter tells EclipseLink to use default connection pool for both reads and writes.
Related
I have recently migrated to JPA 2.0 (EclipseLink 2.4.2 Juno) in TomEE 1.7 in Eclipse IDE, As i am storing the values they are getting stored and retrieved fine, but they are not persisting into the database when is use entitymanager.flush()it is showing javax.persistence.TransactionRequiredException Here is my code
Create.java (register method)
public static int register(String first, String last, String email,
String date, String phone, String address, String pin, Login login) {
try {
System.out.println("registering persisting the entity");
EntityManagerFactory emf = Persistence
.createEntityManagerFactory("FirstEE");
EntityManager manager = emf.createEntityManager();
manager.getTransaction().begin();
//
// Query query = manager
// .createQuery("select l from Truck l");
Login log = login;
System.out.println(log.getUsername() + "username"
+ log.getPassword() + "password");
User reg = new User();
reg.setLogin(log);
reg.setDate(date);
reg.setEmail(email);
reg.setFirst(first);
reg.setLast(last);
reg.setPhone(phone);
reg.setAddress(address);
reg.setPin(pin);
manager.persist(reg);
manager.getTransaction().commit();
manager.flush();
manager.close();
emf.close();
// FacesContext.getCurrentInstance().addMessage("reg:result",
// new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Message",
// "Registered Successfully"));
FacesContext facesContext = FacesContext.getCurrentInstance();
FacesMessage facesMessage = new FacesMessage(
"Registered Successfully");
facesContext.addMessage(null, facesMessage);
System.out.println("after message global");
return 1;
} catch (Exception e) {
System.out.println("hai this is exception caught:" + e);
System.out.println("hai" + e.getMessage());
FacesContext.getCurrentInstance().addMessage(
"reg:result",
new FacesMessage("Something went wrong",
"\tSomething went wrong\t"));
// FacesContext facesContext = FacesContext.getCurrentInstance();
// FacesMessage facesMessage = new
// FacesMessage("Something went wrong");
// facesContext.addMessage(null, facesMessage);
}
return 0;
}
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="FirstEE" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<non-jta-data-source>FirstEE</non-jta-data-source>
<!-- <exclude-unlisted-classes>false</exclude-unlisted-classes> -->
<class>com.jason.Entity.User</class>
<class>com.jason.ManagedBean.Login</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/yash" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="root" />
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="eclipselink.logging.level" value="FINEST" />
<property name="eclipselink.ddl-generation" value="create-tables" />
</properties>
</persistence-unit>
</persistence>
I cant figure out the problem data is getting retreived and stored but it is not updating and not getting persisted in database
The commit()method is committing your transaction immediately. Therefore, the changes are writting into the database & your previously open transaction also ends in this moment. When you call flush() afterwards, there is no open transaction so the flush() operation - complains as you experience it - with a javax.persistence.TransactionRequiredException. See also javax.persistence
Interface EntityManager#flush()
void flush()
Synchronize the persistence context to the underlying database.
Throws:
TransactionRequiredException - if there is no transaction
You should call the commit() method after you synced the state of your EntityManager, like so
manager.getTransaction.begin();
// ...
// do something with your entities in between
// ...
manager.persist(reg);
manager.flush(); // in your case: could also be skipped
// finally - if nothing fails - we are safe to commit
manager.getTransaction().commit();
Another hint:
You should avoid to mix your UI-side code (JSF...) with Backend code. This is generally considered 'spaghetti' anti-pattern here.
Moreover, do not open and close EntityManagerFactory every time (!) you call this UI-bound method (register(...)), as this is a performance killer - at least it produces unnecessary processing every time somebody tries to register here. At least, you should create an instance of EntityManagerFactory (or: EntityManager) as a field (via a DBService class) and reuse this to communicate with the application backend.
The error indicates you cant call flush outside of a transaction. Your code clearly shows you are calling flush right after calling manager.getTransaction().commit(). There is no need to call flush after commit, since the entityManager synchronizes to the database on commit or flush. You only use flush if you have a long running transaction and you want to synchronize at particular stages.
For the rest, turn on EclipseLink logging https://wiki.eclipse.org/EclipseLink/Examples/JPA/Logging and it will show you want it is doing on the commit/flush calls.
I was stuck in very hard situation for a moment.
The problem is: I have to use NativeQuery to delete and/or update some DB records in a JTA context (EJB).
my JPA persistence.xml looks like:
<persistence-unit name="OzsscJPANBPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/postgres_ozssc</jta-data-source>
<mapping-file>com/longz/ozssc/model/PostcodeEntity.xml</mapping-file>
<class>com.longz.ozssc.model.CustomerEntity</class>
......
If I use delete statement in this way:
#Override
public void remove(SmsdOutboxEntity toberemoved){
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
TransactionRequiredException thrown:
root cause
javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
Such there is no transaction existing.
If we use transaction manually as:
#Override
public void remove(SmsdOutboxEntity toberemoved){
em.getTransaction().begin();
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
/*em.flush();*/
em.getTransaction().commit();
IllegalStateException thrown:
root cause
javax.ejb.EJBException: EJB Exception: ; nested exception is:
java.lang.IllegalStateException: The method public abstract javax.persistence.EntityTransaction javax.persistence.EntityManager.getTransaction() cannot be invoked in the context of a JTA EntityManager.
Seems I can't use transaction manually, as JTA will manage transaction their self.
So my question is: How I can use Native Query to delete/update records in a JTA managed context?
Please advise.
For JTA the transaction is provided by the container and you should not create it by yourself. To obtain the transaction in JTA, you have to access it as a resource:
#Resource public UserTransaction utx;
#Resource public EntityManagerFactory factory;
#Override
public void remove(SmsdOutboxEntity toberemoved){
EntityManager em = factory.createEntityManager();
try {
em.createNativeQuery("Delete from outbox where \"ID\" = " + toberemoved.getId()).executeUpdate();
utx.commit();
catch (RuntimeException e) {
if (utx != null) utx.rollback();
throw e; // or display error message
}
finally {
em.close();
}
If you want to omit the part where you have commit/rollback the transaction by yourself, you need to have your transaction managed by some container like EJB3. For that you can use stateless/stateful ejb beans.
UPDATE:
For WebLogic, try to use its user weblogic.transaction.UserTransaction instead of javax.transaction.UserTransaction. As the doc says:
This interface defines WebLogic-specific extensions to
javax.transaction.UserTransaction.
I 'm using Spring Batch(3.0.1.RELEASE) / JPA and an HSQLBD server database.
I need to browse an entire table (using paging) and update items (one by one). So I used a jpaPagingItemReader. But when I run the job I can see that some rows are skipped, and the number of skipped rows is equal to the page size. For i.e. if my table has 12 rows and the jpaPagingItemReader.pagesize = 3 the job will read : lines 1,2,3 then lines 7,8,9 (so skip the lines 4,5,6)…
Could you tell me what is wrong in my code/configuration, or maybe it's an issue with HSQLDB paging?
Below is my code:
[EDIT] : The problem is with my ItemProcessor that performs modification to the POJOs Entities. Since JPAPagingItemReader made a flush between each reading, the Entities are updated ((this is what I want) . But it seems that the cursor paging is also incremented (as can be seen in the log: row ID 4, 5 and 6 have been skipped). How can I manage this issue ?
#Configuration
#EnableBatchProcessing(modular=true)
public class AppBatchConfig {
#Inject
private InfrastructureConfiguration infrastructureConfiguration;
#Inject private JobBuilderFactory jobs;
#Inject private StepBuilderFactory steps;
#Bean public Job job() {
return jobs.get("Myjob1").start(step1()).build();
}
#Bean public Step step1() {
return steps.get("step1")
.<SNUserPerCampaign, SNUserPerCampaign> chunk(0)
.reader(reader()).processor(processor()).build();
}
#Bean(destroyMethod = "")
#JobScope
public ItemStreamReader<SNUserPerCampaign> reader() String trigramme) {
JpaPagingItemReader reader = new JpaPagingItemReader();
reader.setEntityManagerFactory(infrastructureConfiguration.getEntityManagerFactory());
reader.setQueryString("select t from SNUserPerCampaign t where t.isactive=true");
reader.setPageSize(3));
return reader;
}
#Bean #JobScope
public ItemProcessor<SNUserPerCampaign, SNUserPerCampaign> processor() {
return new MyItemProcessor();
}
}
#Configuration
#EnableBatchProcessing
public class StandaloneInfrastructureConfiguration implements InfrastructureConfiguration {
#Inject private EntityManagerFactory emf;
#Override
public EntityManagerFactory getEntityManagerFactory() {
return emf;
}
}
from my ItemProcessor:
#Override
public SNUserPerCampaign process(SNUserPerCampaign item) throws Exception {
//do some stuff …
//then if (condition) update the Entity pojo :
item.setModificationDate(new Timestamp(System.currentTimeMillis());
item.setIsactive = false;
}
from Spring xml config file:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001/MYAppDB" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
trace/log summarized :
11:16:05.728 TRACE MyItemProcessor - item processed: snUserInternalId=1]
11:16:06.038 TRACE MyItemProcessor - item processed: snUserInternalId=2]
11:16:06.350 TRACE MyItemProcessor - item processed: snUserInternalId=3]
11:16:06.674 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.677 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.679 DEBUG SQL- update SNUSER_CAMPAIGN set ...etc...
11:16:06.681 DEBUG SQL- select ...etc... from SNUSER_CAMPAIGN snuserperc0_
11:16:06.687 TRACE MyItemProcessor - item processed: snUserInternalId=7]
11:16:06.998 TRACE MyItemProcessor - item processed: snUserInternalId=8]
11:16:07.314 TRACE MyItemProcessor - item processed: snUserInternalId=9]
org.springframework.batch.item.database.JpaPagingItemReader creates is own entityManager instance
(from org.springframework.batch.item.database.JpaPagingItemReader#doOpen) :
entityManager = entityManagerFactory.createEntityManager(jpaPropertyMap);
If you are within a transaction, as it seems to be, reader entities are not detached
(from org.springframework.batch.item.database.JpaPagingItemReader#doReadPage):
if (!transacted) {
List<T> queryResult = query.getResultList();
for (T entity : queryResult) {
entityManager.detach(entity);
results.add(entity);
}//end if
} else {
results.addAll(query.getResultList());
tx.commit();
}
For this reason, when you update an item into processor, or writer, this item is still managed by reader's entityManager.
When the item reader reads the next chunk of data, it flushes the context to the database.
So, if we look at your case, after the first chunk of data processes, we have in database:
|id|active
|1 | false
|2 | false
|3 | false
org.springframework.batch.item.database.JpaPagingItemReader uses limit & offset to retrieve paginated data. So the next select created by the reader looks like :
select * from table where active = true offset 3 limits 3.
Reader will miss the items with id 4,5,6, because they are now the first rows retrieved by database.
What you can do, as a workaround, is to use jdbc implementation (org.springframework.batch.item.database.JdbcPagingItemReader) as it does not use limit & offset. It is based on a sorted column (typically the id column), so you will not miss any data.
Of course, you will have to update your data into the writer (using either JPA ou pure JDBC implementation)
Reader will be more verbose:
#Bean
public ItemReader<? extends Entity> reader() {
JdbcPagingItemReader<Entity> reader = new JdbcPagingItemReader<Entity>();
final SqlPagingQueryProviderFactoryBean sqlPagingQueryProviderFactoryBean = new SqlPagingQueryProviderFactoryBean();
sqlPagingQueryProviderFactoryBean.setDataSource(dataSource);
sqlPagingQueryProviderFactoryBean.setSelectClause("select *");
sqlPagingQueryProviderFactoryBean.setFromClause("from <your table name>");
sqlPagingQueryProviderFactoryBean.setWhereClause("where active = true");
sqlPagingQueryProviderFactoryBean.setSortKey("id");
try {
reader.setQueryProvider(sqlPagingQueryProviderFactoryBean.getObject());
} catch (Exception e) {
e.printStackTrace();
}
reader.setDataSource(dataSource);
reader.setPageSize(3);
reader.setRowMapper(new BeanPropertyRowMapper<Entity>(Entity.class));
return reader;
I faced the same case, my reader was a JpaPagingItemReader that queried on a field that was updated in the writer. Consequently skipping half of the items that needed to be updated, due to the page window progressing while the items already read were not in the reader scope anymore.
The simplest workaround for me was to override getPage method on the JpaPagingItemReader to always return the first page.
JpaPagingItemReader<XXXXX> jpaPagingItemReader = new JpaPagingItemReader() {
#Override
public int getPage() {
return 0;
}
};
A couple things to note:
All entities that are returned from the JpaPagingItemReader are detached. We accomplish this in one of two ways. We either create a transaction before querying for the page, then commit the transaction (which detaches all entities associated with the EntityManager for that transaction) or we explicitly call entityManager.detach. We do this so that features like retry and skip can be correctly performed.
While you didn't post all the code in your processor, my hunch is that in the //do some stuff section, your item is getting re-attached which is why the update is occurring. However, without being able to see that code, I can't be sure.
In either case, using an explicit ItemWriter should be done. In fact, I consider it a bug that we don't require an ItemWriter when using java config (we do for XML).
For your specific issue of missing records, you need to keep in mind that a cursor isn't used by any of the *PagingItemReaders. They all execute independent queries for each page of data. So if you update the underlying data in between each page, it can have an impact on the items returned in future pages. For example, if my paging query specifies where val1 > 4 and I have a record that val1 was 1 to be 5, in chunk 2, that item may be returned since it now meets the criteria. If you need to update values that are in your where clause (thereby impacting what falls into the set of data you'd be processing), it's best to add a processed flag of some kind that you can query by instead.
I had the same problem with rows being skipped based on the pageSize.
If I have pageSize set to 2 for example, it would read 2, ignore 2, read 2, ignore 2 etc.
I was building a daemon processor to poll a 'Request' database table for records at a 'Waiting To Be Processed' status. The daemon is designed to run for ever in the background.
I had a 'status' field which was defined in the #NamedQuery and would select records whose status was '10':Waiting to be processed. After the record was processed, the status field would be updated to '20':Error or '30':Success.
This turned out to be the cause of the problem - I was updating a field which was defined in the query. If I introduced a 'processedField' and updated that instead of the 'status' field then no problem - all the records would be read.
As a possible solution to updating the status field, I setMaxItemCount to be the same as the PageSize; this updated the records correctly before step completion. I then keep executing the step until a request is made to stop the daemon. OK, probably not the most efficient way to do it (but I’m still benefiting from the ease of use that JPA provides) but I think it would probably be better to use JdbcPagingItemReader (described above – thanks!). Opinions on the best approach to this batch database polling problem would be welcome :)
Hello everybody i'm looking for some help with the next problem:
i have a jpa/stateless ejb's proyect that works perfectly, it just does simple queries and persist operations, but now i need to execute a set of persist operations, if any of them fails, i must perform a rollback, so i found JTA can do the job but using this piece of source code:
#Stateless
public class ProjectBean implements IProject {
#Resource
javax.transaction.UserTransaction utx;
#PersistenceContext(unitName = "JPADB")
private EntityManager entityManager;
...
//more code
//this is part of a method
try{
utx.begin();
entityManager.joinTransaction();
for(Project p:projectResultList){
entityManager.persist(p);
}
utx.commit();
}catch(Exception e){
e.printStackTrace();
if(utx != null)
try {
utx.rollback();
} catch (IllegalStateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SecurityException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (SystemException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//tx.rollback();
}
simply doesn't work, and this is how the persistence.xml looks like:
<persistence-unit name="JPADB">
<jta-data-source>java:jboss/datasources/OracleBic</jta-data-source>
<properties>
<property name="hibernate.show_sql" value ="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
</properties>
</persistence-unit>
really hope anyone can give me a tip or advice, i'm a newbie with jpa/jta concepts and i tried lots of codes i found in the web but i always obtain different errors (Wrong tx on thread: expected TransactionImple usertransaction begin,Cannot use an EntityTransaction while using JTA). thanks in advance.
Did you instruct your AS that you are going to handle transactions manually with
#TransactionManagement(TransactionManagementType.BEAN) on method level? I do not see the annotation on class level. Probably you have it on method level, but your code snipped is insufficient to make any guess.
Otherwise all transactions are Container managed and your code is not going to work. So you have to either put
#TransactionManagement(TransactionManagementType.BEAN)
on either a method or class level depends on requirements or you may want to allow your container to manage transactions for you and than you have to make changes that #remigio suggested to you.
It seems that the second approach is better in your case
remigio's comment is correct, #Stateless session beans control transaction boundaries with javax.ejb.#TransactionAttribute, if the annotation is absent all public methods are TransactionAttribute.REQUIRED. For more information, see http://download.oracle.com/otndocs/jcp/ejb-3_0-fr-eval-oth-JSpec/ (ejb-3_0-fr-spec-ejbcore.pdf).
UserTransaction is never used in a #Stateless session bean, but rather is used by clients that are calling the bean to demarcate a wider transaction window than that of just the method call itself.
Today it's the first time I'm using GWT and JDO. I am running it with Eclipse in the local debug mode.
I do the following thing:
public Collection<MyObject> add(MyObject o) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(o);
Query query = pm.newQuery(MyObject.class);// fetch all objects incl. o. But o only sometimes comes...
List<MyObject> rs = (List<MyObject>) query.execute();
ArrayList<MyObject> list= new ArrayList<MyObject>();
for (MyObject r : rs) {
list.add(r);
}
return list;
} finally {
pm.close();
}
}
I already set <property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" /> in my jdoconfig.xml. Do I have to set some other transaction stuff in the config? Was somebody got a working jdoconfig.xml? Or is the problem somewhere else? Some caching inbetween?
EDIT: Things I have tried:
Setting NontransactionalRead/Write to false
Using the same/a different PersistenceManager though calling PMF.get().getPersistenceManager() multiple times
Using transactions
ignoreCache = true on PersistenceManager
calling flush and checkConsistency
The jdoconfig:
<persistence-manager-factory name="transactions-optional">
<property name="datanucleus.appengine.datastoreReadConsistency" value="STRONG" />
<property name="javax.jdo.PersistenceManagerFactoryClass"
value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
<property name="javax.jdo.option.ConnectionURL" value="appengine"/>
<property name="javax.jdo.option.NontransactionalRead" value="true"/>
<property name="javax.jdo.option.NontransactionalWrite" value="true"/>
<property name="javax.jdo.option.RetainValues" value="true"/>
<property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
</persistence-manager-factory>
I must be missing something central here because all approaches fail...
EDIT2: When I split the job into two transaction the log says that the write transaction fished and then the read transaction starts. But it doesn't find the just persited object. It always says Level 1 Cache of type "weak" initialised aswell. Is week bad or good?
It about 30% of requests that go wrong... Might I be some lazy query loading issue?
Franz, the Default read consistency in the JDO Config is STRONG. so if you are trying to approach it in that direction, it wont lead you anywhere
Check this out as i think it mentions something similar to the scenario which you are encountering, with the committed data not returned back in the query. It isnt concurrent as mentioned, but it explains the commit process.
http://code.google.com/appengine/articles/transaction_isolation.html
Also, another approach would be to query using Extents and find out if that solves the particular use case you are looking at, since i believe you are pulling out all the records in the table.
EDIT :
Since in the code snippet that you have mentioned, it queries the entire table. And if that is what you need, you can use an Extent...
The way to use it is by calling
Extent ext = getExtent(<Entity Class name>)
on the persistenceManager singleton object. You can then iterate through the Extent
Check out the documentation and search for Extents on the page here.
http://code.google.com/appengine/docs/java/datastore/jdo/queries.html
Calling the makePersistent() method doesn't write to the datastore; closing the PersistenceManager or committing your changes does. Since you haven't done this when you run your query, you're getting all objects from the datastore which does not, yet, include the object you just called makePersistent on.
Read about object states here:
http://db.apache.org/jdo/state_transition.html
There are two ways around this, you can put this inside a transaction since the commit writes to the datastore (keep in mind GAE 5 transaction/entity type limit on transactions) and commit before running your query;
Example using transaction...
public Collection<MyObject> add(MyObject o) {
PersistenceManager pm = PMF.get().getPersistenceManager();
ArrayList<MyObject> list = null;
try {
Transaction tx=pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(o);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
}
Query query = pm.newQuery(MyObject.class);
List<MyObject> rs = (List<MyObject>) query.execute();
ArrayList<MyObject> list = new ArrayList<MyObject>();
for (MyObject r : rs) {
list.add(r);
}
} finally {
pm.close();
}
return list;
}
or you could close the persistence manager after calling makePersistent on o and then open another one to run your query on.
// Note that this only works assuming the makePersistent call is successful
public Collection<MyObject> add(MyObject o) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(o);
} finally {
pm.close();
}
pm = PMF.get().getPersistenceManager();
ArrayList<MyObject> list = null;
try {
Query query = pm.newQuery(MyObject.class);
List<MyObject> rs = (List<MyObject>) query.execute();
list= new ArrayList<MyObject>();
for (MyObject r : rs) {
list.add(r);
}
} finally {
pm.close();
}
return list;
}
NOTE: I originally said you could just add o to the result list before returning; but that isn't a smart thing to do since in the event that there is a problem writing o to the datastore; then the returned list wouldn't reflect the actual data in the datastore. Doing what I now have (committing a transaction or closing the pm and then getting another one) should work since you have your datastoreReadPolicy set to STRONG.
I encountered the same problem and this didn't help. Since it seems to be the top result on Google for "jdo app engine consistency in eclipse" I figured I would share the fix for me!
Turns out I was using multiple instances the PersistenceManagerFactory which led to some bizarre behaviour. The fix is to have a singleton that every piece of code accesses. This is in fact documented correctly on the GAE tutorials but I think it's importance is understated.
Getting a PersistenceManager Instance
An app interacts with JDO using an instance of the PersistenceManager
class. You get this instance by instantiating and calling a method on
an instance of the PersistenceManagerFactory class. The factory uses
the JDO configuration to create PersistenceManager instances.
Because a PersistenceManagerFactory instance takes time to initialize,
an app should reuse a single instance. An easy way to manage the
PersistenceManagerFactory instance is to create a singleton wrapper
class with a static instance, as follows:
PMF.java
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() {}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}