You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Daniel Jue <te...@gmail.com> on 2014/01/11 02:01:50 UTC
Neo4J 2.0 transactions on domain objects with T5.3.x
Hello friends,
I'm upgrading some code of mine to use Neo4J 2.0, which now requires a
transaction on _reads_ as well as writes for anything dealing with
Node/Relationships. (It used to be reading didn't require a tx)
For those that don't know Neo4J nodes and edges have a Map<String,Object>
property inside them, and that's where you keep your data that would
normally end up in a domain object as fields.
A recent suggestion from the Neo4J site for working with Domain objects
(Let's say, User.class for example) is to store the underlying node in the
domain object and have getters/setters that work on the underlying node's
property map. I've used that suggestion this evening and I'm wondering if
this is the right decision. Here's more information:
A graph service is required to create transactions. I have this available
by IOC, no problem.
I also have an IOC'd UserDAO which generates User.class objects from the
graph database. Again, no problem there. Once the UserDAO returns a
User.class to whoever asked for it, that User.class can't be read/written
to without a transaction from the graph service.
I don't want the domain objects to have a handle on the graph service, that
doesn't seem right to me. I can't advise the domain objects because they
are not covered by the IOC framework.
Basically anything that can touch the domain object now has to have the
ability to create a transaction. This seems like an ideal job for a custom
@Neo4JTransactional, which I'd have to add to every page/component/service
that might touch a User.class.
Whereas with T5Hibernate, @CommitAfter is optional, this new
@Neo4JTransactional would be required or else it will fail. There are
plenty of times I just needed to read fields from a bean when using
Hibernate, and I didn't have to worry about a transaction.
For classes that are not 'covered' by IOC, such as Test classes, I'd have
to get my transaction elsewhere, probably from the same DAO I get the
User.class from.
The Question:
So does a custom Advise annotation seem like the right way to go? I'd
create a tx before the method invocation, invoke it and then call
tx.success afterwards, for all methods in a class. It would be a pain (and
intrusive) to have to mark each method that could possibly read or write to
the domain object, so I'd want to Advise the class on all methods. Luckily
Neo4J doesn't seem to care about nested tx, so that shouldn't be a problem!
I've looked briefly at how Spring's Neo4J Data framework handles this.
Looks like it might work, but I can't add that dependency to the project
right now. I could maybe borrow some code though.
The other option, which I had been using previously, is this:
===================
The DAO returns a User.class that has no idea what a Neo4J Node is. Neo4J
can't 'detach' a node it seems, so I iterated through the
Map<String,Object> and set all the properties manually in the User.class
Now a T5 page or service comes along and mucks with the User object, but to
save it back to the graph db I have to essentially give the User object
back to the DAO and manually reattach it (lookup the original node and
update properties).
1) This creates a lot of boiler plate for the detach/re-attach.
2) This becomes messy if the property that is getting updated is the same
one we used to look up the Node (i.e. if the lookup value was the email
address, which can be changed) I don't really have that case now, but I
could foresee that problem with the other domain objects.
===================
Thanks for any tips or wisdom,
Dan
Re: Neo4J 2.0 transactions on domain objects with T5.3.x
Posted by Daniel Jue <te...@gmail.com>.
Thanks++;
I kinda of had it working, but it was too much trouble to maintain which
classes were getting advised. Also I had the issue of talking to multiple
embedded Neo4J instances (something not many people do), and different
transactional advice is needed depending on where the attached Node came
from. (i.e. a Neo4J Node from database A needs transactional advise from
database A). This worked as well using Markers but further complicated the
issue, and it wouldn't scale well when we have several graph databases to
talk to at once.
Here's what I ended up doing in case people follow in my footsteps with
embedded Neo4J and T5:
In the end I just created detached domain POJOs, and adjusted my DAOs to
return those instead. Now I explicitly save the detached by
dao.update(myDetachedObj), and the DAO worries about the transaction, etc.
It's very simple, seems that's the way nature intended. Hahaha
In other exciting news, I received official word that the particular set of
code I've been working on will be released as open source on Github. So
I've got that going for me. :-)
On Tue, Jan 14, 2014 at 6:49 AM, Thiago H de Paula Figueiredo <
thiagohp@gmail.com> wrote:
> On Mon, 13 Jan 2014 22:20:41 -0200, Daniel Jue <te...@gmail.com> wrote:
>
> Thanks Thiago!
>>
>
> My pleasure!
>
>
> Now I want to apply it to all my pages and components in my web module
>> using @Match("*"). It causes a recursion error since it sees the
>> services in the DAO module which are needed to create the transaction!
>>
>
> Pages and components aren't services, so I don't think using @Match("*")
> will work for them. I guess you'll need to write a
> ComponentClassTransformWorker to advise page and component methods. See the
> source of CachedWorker for an example.
>
>
> One more thing: When I have one of these Neo4J-backed domain objects
>> (let's say User.class), I might have something like this in a page:
>>
>> @Property
>> private User theUser;
>>
>> In my template, if I do ${theUser.firstName}, will the transactional
>> advise be applied to the automatically constructed getters and setters for
>> that
>> property?
>>
>
> No.
>
>
> --
> Thiago H. de Paula Figueiredo
> Tapestry, Java and Hibernate consultant and developer
> http://machina.com.br
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>
Re: Neo4J 2.0 transactions on domain objects with T5.3.x
Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Mon, 13 Jan 2014 22:20:41 -0200, Daniel Jue <te...@gmail.com> wrote:
> Thanks Thiago!
My pleasure!
> Now I want to apply it to all my pages and components in my web module
> using @Match("*"). It causes a recursion error since it sees the
> services in the DAO module which are needed to create the transaction!
Pages and components aren't services, so I don't think using @Match("*")
will work for them. I guess you'll need to write a
ComponentClassTransformWorker to advise page and component methods. See
the source of CachedWorker for an example.
> One more thing: When I have one of these Neo4J-backed domain objects
> (let's say User.class), I might have something like this in a page:
>
> @Property
> private User theUser;
>
> In my template, if I do ${theUser.firstName}, will the transactional
> advise be applied to the automatically constructed getters and setters
> for that
> property?
No.
--
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org
Re: Neo4J 2.0 transactions on domain objects with T5.3.x
Posted by Daniel Jue <te...@gmail.com>.
Thanks Thiago!
I have it working for my tests in my DAOModule (no classes, just services
that are explicitly advised via @Match("prefix*")
Now I want to apply it to all my pages and components in my web module
using @Match("*"). It causes a recursion error since it sees the services
in the DAO module which are needed to create the transaction!
I could do this for all my pages and components:
@Match({ "Page1", "Page2", "Page3",...
"Component1","Component2","Component3"... })
But that smells bad. Would that be your choice?
I could also do @Advise(serviceInterface = Page1.class) , but that's even
worse because @Advise only takes one class at a time.
What I kind of want is to advise all my pages and components.
One more thing: When I have one of these Neo4J-backed domain objects
(let's say User.class), I might have something like this in a page:
@Property
private User theUser;
In my template, if I do ${theUser.firstName}, will the transactional advise
be applied to the automatically constructed getters and setters for that
property?
BTW My annotation looks like this:
package org.apache.tapestry5.neo4j.annotation;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.BEAN;
import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.MIXIN;
import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
import static
org.apache.tapestry5.ioc.annotations.AnnotationUseContext.SERVICE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.apache.tapestry5.ioc.annotations.UseWith;
/**
* Marks a method of a service (or a component method) as transactional: the
* call will create a tx, invoke the method, and then should call
tx.success()
* after invoking the method. Failure through exception will cause Neo4J to
abort the commit.
*
* @author djue
*
*/
@Target({METHOD})
@Retention(RUNTIME)
@Documented
@UseWith({ COMPONENT, MIXIN, PAGE, SERVICE, BEAN })
public @interface Neo4JTransactional {
}
On Mon, Jan 13, 2014 at 6:35 AM, Thiago H de Paula Figueiredo <
thiagohp@gmail.com> wrote:
> On Fri, 10 Jan 2014 23:01:50 -0200, Daniel Jue <te...@gmail.com> wrote:
>
> Hello friends,
>>
>
> The Question:
>> So does a custom Advise annotation seem like the right way to go?
>>
>
> Yes! Just remember that Tapestry-IoC 5.3 doesn't copy the annotations from
> service implementation and its methods to the service proxy, but that
> limitation was already fixed in 5.4. For 5.3, you'll need to put your
> annotation in the service interface.
>
> --
> Thiago H. de Paula Figueiredo
> Tapestry, Java and Hibernate consultant and developer
> http://machina.com.br
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>
Re: Neo4J 2.0 transactions on domain objects with T5.3.x
Posted by Thiago H de Paula Figueiredo <th...@gmail.com>.
On Fri, 10 Jan 2014 23:01:50 -0200, Daniel Jue <te...@gmail.com> wrote:
> Hello friends,
> The Question:
> So does a custom Advise annotation seem like the right way to go?
Yes! Just remember that Tapestry-IoC 5.3 doesn't copy the annotations from
service implementation and its methods to the service proxy, but that
limitation was already fixed in 5.4. For 5.3, you'll need to put your
annotation in the service interface.
--
Thiago H. de Paula Figueiredo
Tapestry, Java and Hibernate consultant and developer
http://machina.com.br
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org