Persist object without try-catch block in method - postgresql

I'm currently working on a project and I want to see what damage it can do if I don't embrace my code with try-catch block when persisting object into database. Here is my code down below that i use as test.
public class NewEventServiceBean implements NewEventService {
#PersistenceContext(name = "example")
EntityManager manager;
#Resource
private UserTransaction userTransaction;
#Override
public void createNewEvent(String title, String content) throws Exception
{
userTransaction.begin();
Event event = new Event();
Content cont = new Content();
cont.setContent(content);
event.setTitle(title);
event.setCont(cont);
manager.persist(event);
manager.persist(cont);
userTransaction.commit();
}
In the database i have this Event table that has a foreign key to Content table.
And my question is if It's possible that Event object is persisted in to the database even if I cause something wrong when persisting the content class. Or what is the disadvantages of not embracing the code with try catch and rollback?
I've tried to cause an error when persisting the content object, but the Event is not persisted into the datbase even if everything is correct in that class.

Related

Cannot remove a JPA entity using Spring Integration

When I try to remove an entity using Outbound Channel Adapter I always get removing a detached instance exception.
I know that an entity should be retrieved and deleted in the same transaction to avoid this exception, but how can I achieve it with Spring Integration?
To demonstrate the problem I modified the JPA sample:
PersonService.java
public interface PersonService {
...
void deletePerson(Person person);
}
Main.java
private static void deletePerson(final PersonService service) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
spring-integration-context.xml
<int:gateway id="personService"
service-interface="org.springframework.integration.samples.jpa.service.PersonService"
default-request-timeout="5000" default-reply-timeout="5000">
<int:method name="createPerson" request-channel="createPersonRequestChannel"/>
<int:method name="findPeople" request-channel="listPeopleRequestChannel"/>
<int:method name="deletePerson" request-channel="deletePersonChannel"/>
</int:gateway>
<int:channel id="deletePersonChannel"/>
<int-jpa:outbound-channel-adapter entity-manager-factory="entityManagerFactory"
channel="deletePersonChannel" persist-mode="DELETE" >
<int-jpa:transactional transaction-manager="transactionManager" />
</int-jpa:outbound-channel-adapter>
When I call deletePerson I get the exception:
Exception in thread "main" java.lang.IllegalArgumentException:
Removing a detached instance
org.springframework.integration.samples.jpa.Person#1001
UPDATE:
Apparently I should've chosen a sample closer to my actual project, because here you can just create a new transaction programmatically and wrap both retrieve and delete function calls in it, as Artem did.
In my project I have a transformer connected to an outbound-channel-adapter. The transformer retrieves an entity and the outbound-channel-adapter removes it. How can I get the transformer and the outbound-channel-adapter to use the same transaction in this case?
To get it worked you should wrap all operations in the deletePerson to transaction, e.g.
private static void deletePerson(final PersonService service) {
new new TransactionTemplate(transactionManager)
.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
final List<Person> people = service.findPeople();
Person p1 = people.get(0);
service.deletePerson(p1);
}
});
}
In this case you should somehow provide to your method transactionManager bean too.
UPDATE:
I shown you a sample for use-case in the original question.
Now re. <transformer> -> <jpa:outbound-channel-adapter>.
In this you should understand where your message flow is started:
If it is <inbound-channel-adapter> with poller, so just make the <poller> <transactional>
If it <gateway>, who call <transformer>, so it's just enough to mark gateway's method with #Transactional
Here is one more transactional advice trick: Keep transaction within Spring Integration flow
In all cases you should get rid of <transactional> from your <jpa:outbound-channel-adapter>

Updating entities in Extended Persistence Context

I have a form - Workflow where there are fields like wfName, assignedUser, dueDate, turnAroundTime. etc.
It is backed by an entity Workflow with a reference to the User entity as Many-to-One.
When a change is made to the assignedUser field( it is an email address) and the form is submitted, I get a Unique-constraint violation error on the USER entity.
I am not trying to achieve this. I only want to replace the User in the Workflow entity.
The save function is performed by a Stateful session bean, with an EXTENDED persistence context.
Am I missing something here? Is this the correct way to updated information in a referenced field?
While setting the updated User I am doing
User user = workflow.getUser();
//This user has its email address changed on the screen so getting a fresh reference of the new user from the database.
user = entitManager.createQuer("from User where email_address=:email_address").setParameter("email_address", user.getEmailAddress).getSingleResult();
//This new found user is then put back into the Workflow entity.
workflow.setUser(user);
entityManager.merge(workflow);
No exception is thrown at the time these lines are executed, but later in the logs I find that it threw a
Caused by: java.sql.SQLException: ORA-00001: unique constraint (PROJ.UK_USER_ID) violated
There is no cascading configuration present in the entities.
The following is the association code for the entities-
The workflow-User relation
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID", nullable = false)
#NotNull
public GwpsUser getUser() {
return user;
}
public void setUserByUserId(User user) {
this.user = user;
}
The User-Workflow Relation
#OneToMany(fetch = FetchType.LAZY, mappedBy = "User")
public Set<Workflow> getWorkflowsForUserId() {
return workflowsForUserId;
}
public void setWorkflowsForUserId(
final Set<Workflow> WorkflowsForUserId) {
this.workflowsForUserId = workflowsForUserId;
}
In the SFSB I have two methods loadWorkflow() and saveWorkflow().
#Begin(join = true)
#Transactional
public boolean loadProofData(){
//Loading the DataModel here and the conversation starts
}
If I add flushMode = FlushModeType.MANUAL inside #Begin. The saveWorkflow() method saves the data properly, only for the first time. I have to go somewhere else and then come back to this page if I want to make any further changes.
The saveWorkflow() method looks like
#SuppressWarnings("unchecked")
public boolean saveWorkflow() throws FileTransferException {
//Do some other validations
for (Workflow currentWorkflow : workflowData) {
User user = currentWorkflow.getUser();
//This user has its email address changed on the screen so getting a fresh reference of the new user from the database.
user = entitManager.createQuery("from User where email_address=:email_address").setParameter("email_address", user.getEmailAddress).getSingleResult();
//This new found user is then put back into the Workflow entity.
currentWorkflow.setUser(user);
}
//Do some other things
}
Not using the merge() method here, but still the problem persists.
Why are you calling merge? Is the workflow detached (serialized)?
If it is not detched, you should not call merge, just change the object and it should be updated.
You should have a setUser method, not setUserByUserId? Not sure how this is working, perhaps include your full code. Your get/set method might be corrupting your objects, in general it is safer to annotate fields instead of method to avoid code in your get/set method to cause odd side-effects.
Ensure you are not creating two copies of the object, it seems your merge is somehow doing this. Enable logging and include the SQL. Calling flush() directly after your merge will cause any errors to be raise immediately.

Why transaction can't commit in a self-invoked ejb method with #REQUIRES_NEW Annotation

First I want to explain my self-invoked ejb method in this situation. I have a stateful session bean with a method which starts a new transaction (Annotated by #REQUIRES_NEW). To invoke this method inside the bean itself and make the annotation effective, I use SessionContext#getBusinessObject() to achieve the effect of #EJB (#EJB here causes stackoverflow?!). My code is shown below:
#Stateful
#Local
public class TransactionTest implements ITransactionTest {
#PersistenceContext(unitName="Table",Type=PersistenceContextType.EXTENDED)
private EntityManager manager;
#Resource
SessionContext sc;
ITransactionTest me;
#PostConstruct
public void init(){
me = this.sc.getBusinessObject(ITransactionTest.class);
}
public void generateRecord(int i) throws RuntimeException{
Record record = new Record();
record.setId(i+"");
record.status(1);
manager.persist(record);
manager.flush(); //If not flush, result is correct. Why?
me.updateRecord(i);
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void updateRecord(int i) throws RuntimeException{
try {
Record record = manager.find(Record.class, i+"");
record.setStatus(2);
manager.flush();
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
While,generateRecord() runs properly. The console shows it executes 'insert' and 'update' HQL without any exception (I use Hibernate as JPA provider). However, the 'update' result doesn't appear in the database. Why? Does updateRecord() commit correctly?
Also, I try it in two altenative ways: First is invoking generateRecord() (it will no longer invoke updateRecord()) and updateRecord() consecutively in another bean. It can give me the right result.
The second is removing the first flush(). Then both 'insert' and 'update' HQL will be executed at the second flush(). This method can also produce right result.
My program is running under JBOSS 6.1.0-Final and database is Oracle.
Best Regards,
Kajelas

GWT Request Factory and Editor Framework Exception

When attempting to edit a new (proxy) entity using RequestFactoryEditorDriver.edit() I am getting the following error: "Exception caught: Attempting to edit an EntityProxy previously edited by another RequestContext". I am fairly sure that this is a result of my misunderstanding of the request factory/editor framework architecture. Here is the editor code that I think pertains to this problem:
public class OrgMaintenanceWidget extends Composite implements Editor<IOrgProxy> {
... other fields ...
private IOrgEditorDriver _orgEditorDriver;
interface IOrgEditorDriver extends RequestFactoryEditorDriver<IOrgProxy, OrgMaintenanceWidget> {}
public OrgMaintenanceWidget(final IClientFactory clientFactory) {
... widget initialization ...
_orgEditorDriver = GWT.create(IOrgEditorDriver.class);
_orgEditorDriver.initialize(_clientFactory.getRequestFactory().getEventBus(),
_clientFactory.getRequestFactory(), this);
}
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
...
}
It's the "_orgEditorDriver.edit()" line that causes the exception. The "newOrg()" method is:
public IOrgProxy newOrg() {
return _clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
}
The RequestFactory is simply:
public interface IRequestFactory extends RequestFactory {
IOrgRequestContext orgRequestContext();
}
I am sure that I'm missing something fundamental about editing a new entity. When I edit an existing entity everything is fine ... the UI components are populated automatically, and flushing the editor back to the entity works very nicely. Here's the code that initiates editing for an existing entity:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
Any help would be greatly appreciated, and I'll try to publish any lessons learned.
This code:
_clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
Means:
Create new orgRequestContext()
Create new IOrgProxy using this context
Edit new IOrgProxy using this context, because as docs say: "Returns a new mutable proxy that this request can carry to the server, perhaps to be persisted.", it means that the proxy is edited by this request.
This code:
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
Means:
Again, create new orgRequestContext() (because each invocation of getRequestFactory().orgRequestContext() provides new instance of orgRequestContext()
"Start driving the Editor and its sub-editors with data." as docs say. But as a part of it, use passed orgRequestContext() to edit passed IOrgProxy instance, so that the proxy is editable.
Because the proxy was already edited while created by other RequestContext, you get the exception, because there is fundamental rule in RequestFactory, that proxy can be edited only by one RequestContext.
See also this thread.
I think you can't create an object with one RequestContext and then edit it with another one.
So you can solve this in two ways:
Persist the created object with the RequestContext you used when you created the object. The save method should return the persisted object and this persisted object can be passed to the editor with a fresh new RequestContext
Somewhere save the RequestContext you used for creating the object and pass it to the edit function of your Driver
Solution two could look something like this:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
IOrgRequestContext ctx = _clientFactory.getRequestFactory().orgRequestContext();
_org = ctx.create(IOrgProxy.class);
_orgEditorDriver.edit(_org,ctx );
}

How do I test that controllers write correctly to the DB in playframework

I have a FunctionalTest that tests posting to a controller and then does asserts on the model objects to make sure the controller did it's job, like so:
#Test
public void editUser(){
Logger.debug("Edit user test");
createNewUser();
final User user = User.<User>findAll().get(0);
POST("/ManageUser/save", ImmutableMap.of(
"user.id", user.getId().toString(),
"user.username", "test",
"user.email", "test#example.com",
"user.fullName", "Test Different"
));
User.em().flush();
User.em().clear(); // this is required so that it works on the mem DB
assertEquals(1, User.findAll().size());
assertEquals("Test Different", User.<User>findAll().get(0).fullName);
final User userAfterSave = User.<User>findAll().get(0);
assertFalse("New user should not be admin.", userAfterSave.isAdmin);
}
This passes when I use the mem database
%test.db.url=jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
However if I switch to mysql
%test.db=mysql://test:test#localhost/test
It fails on the second assert "Failure, expected:<Test [Differen]t> but was:<Test [Tes]t>". So when using mysql the controller doesn't persist the user properly.
What am I missing here, are there some options for transaction control that I need to change for this to work?
The controller just calls user.merge().save() to update the user, is this somehow wrong?
This is because the mem DB is not properly transactional, meaning the test thread gets new data every read. For mysql however the test thread read the user in createNewUser() meaning it's transaction had the previous version. It's not obvious but POST() starts a new thread with a separate transaction. To solve this swap out
User.em().flush();
User.em().clear();
for
JPAPlugin.closeTx(false);
JPAPlugin.startTx(false);
The later starts a new transaction.
Do your search in a separate job to be sure it correctly view the modifications (transaction isolation). Here is an example
private FeedbackType findFeedbackType(final String name) throws ExecutionException, InterruptedException {
return new Job<FeedbackType>() {
#Override
public FeedbackType doJobWithResult() throws Exception {
return FeedbackType.findByName(name);
}
}.now().get();
}
This is a private method of my Functional test and I call this method to get my object instead of directly invoking the model