Why don't we use entityManager#contains instead of checking the ID?

2014-06-16 10:22 GMT+02:00 Karl Kildén <karl.kil...@gmail.com>:

> Hi,
>
> On could argue that the real problem is that the algorithm that decides if
> it should be a save or persist. It uses some portable way of deciding this
> that requires the entity to be managed.
> That algorithm could be improved in each project.
>
>
>     @Override
>     @RequiresTransaction
>     public E save(E entity)
>     {
>         if (context.isNew(entity))
>         {
>             entityManager().persist(entity);
>             return entity;
>         }
>         return entityManager().merge(entity);
>     }
>
>
>
> I would say that overriding this method would solve EntityExistsException
>  in a cleaner way. For this project I have no natural keys only generated
> long so it would be a cheap and easy way to fix it... This is fully
> sufficient for me:
>
>
>     @Override
>     @RequiresTransaction
>     public E save(E entity)
>     {
>         BaseEntity e = (BaseEntity) entity;
>         if (e.getId == 0)
>         {
>             entityManager().persist(entity);
>             return entity;
>         }
>         return entityManager().merge(entity);
>     }
>
>
>
>
> If not overridden then what happens if the group has changed in my example,
> you are supposed to use entityManager and find it?
>
> Maybe the API should provide something like the utility method I proposed
> then... I will explain it a little better. All you need to do is inject the
> groupMapper or whatever mapper you need. To get the group if it changed you
> simply call fetch with a new Group instance, the DTO with the new
> information and the groupMapper. Why send in new group instance? Well one
> could also send in Group.class and use a reflection to create a new group
> if needed. But new Group() vs Group.class I actually prefer the first
> because it avoids reflection. Because the new Group() argument also allows
> for getClass() the entityManager has all the info required except the id
> but that is no problem since we also send in the mapper with the handy
> #getPrimaryKey
> method.
>
> Group g = fetch (new Group(), user.getGroup(), groupMapper);
>
>     public <Entity, Dto> Entity fetch(Entity entity, Dto dto,
> SimpleQueryInOutMapperBase<Entity, Dto> entityMapper){
>         Object pk = entityMapper.getPrimaryKey(dto);
>         Entity foundEntity = (Entity) entityManager.find(entity.getClass(),
> pk);
>
>         if (foundEntity == null) {
>             foundEntity = entity;
>         }
>         return entityMapper.toEntity(foundEntity, dto);
>     }
>
>
> I have not tested this method at all, but something like it would work well
> together with the default strategy for determine save or merge... But my
> main wish now is to override the save logic actually.
>
>
>
>
>
>
>
>
>
>
>
> On 16 June 2014 09:48, Thomas Hug <thomas....@gmail.com> wrote:
>
> > Thanks Karl for the clarification!
> > If you assign a new group, I'd first make sure you have this one
> persisted
> > as well (or we do too much in the mapper IMO). Then save the userDto with
> > something like
> >
> > User toEntity(User user, UserDto dto) {
> >     ...
> >     user.setGroup(groupMapper.toEntity(dto.getGroup()); // or check
> before
> > if ID changed
> > }
> >
> > I guess that would even work if the group is not persisted if you adapt
> > cascading.
> >
> > Makes sense?
> >
> > On Fri, Jun 13, 2014 at 5:56 PM, Karl Kildén <karl.kil...@gmail.com>
> > wrote:
> >
> > > Not sure I get myself ;)
> > >
> > > Lets walk through how I see it:
> > >
> > > 1. User "foo" is created and is assigned to the preexisting group
> > "Admin".
> > >
> > > It goes like this in the flow: user = new UserDTO() ->
> > > *user*.setGroupDTO(selectedGroup)
> > > -> save
> > >
> > > Some logic must make sure that we don't get EntityExistsException
> trying
> > to
> > > save that group.
> > >
> > >
> > > 2. Many sessions later user "foo" is edited. Flow:
> > > *user*.setGroupDTO(newGroup)
> > > -> save
> > >
> > > The variable *user *is a random object that happens to be the latest
> > > version of that user that also has a new group set.
> > >
> > > So  *PK, user.getGroup()*
> > > *should lazyload the group - right? *This will result in the previous
> > group
> > > being loaded unless I am missing something. While it is technically
> > correct
> > > since the new group relationship has not been persisted yet it is
> > actually
> > > impossible to ever update group with this flow
> > >
> > >
> > >
> > >
> > >
> > >
> > >
> > > On 13 June 2014 17:09, Thomas Hug <thomas....@gmail.com> wrote:
> > >
> > > > Hi Karl
> > > > Sorry not sure if I get you right. Why would user.getGroup() be
> stale?
> > As
> > > > in the update case user has just been fetched by the PK,
> > user.getGroup()
> > > > should lazyload the group - right?
> > > >
> > > >
> > > > On Fri, Jun 13, 2014 at 2:51 PM, Karl Kildén <karl.kil...@gmail.com>
> > > > wrote:
> > > >
> > > > > Hi and hrmmm,
> > > > >
> > > > > What if user group was changed?
> >  groupMapper.toEntity(user.getGroup(),
> > > > > userDto.getGroup()); This would then operate on stale data unless
> you
> > > > fetch
> > > > > and send in correct group. And managing new groups is easy for me I
> > > > think,
> > > > > I would call the method using groupMapper.toEntity(new Group(),
> > > > > userDto.getGroup());
> > > > >
> > > > >
> > > > > I would much prefer all three methods to be non protected. Then I
> > could
> > > > > create my helper something along the lines of this:
> > > > >
> > > > > I did not test this very well but unless I thought wrong completely
> > it
> > > > > would be a one time thing to implement. But using mappers from
> > mappers
> > > > are
> > > > > hard because if the relationship is declared in both ways you can
> get
> > > > > circular references. Anyways here's my helper I theorycrafted
> > together:
> > > > >
> > > > >
> > > > > Group g = fetch (new Group(), user.getGroup(), groupMapper);
> > > > >
> > > > >     public <Entity, Dto> Entity fetch(Entity entity, Dto dto,
> > > > > SimpleQueryInOutMapperBase<Entity, Dto> entityMapper){
> > > > >         Object pk = entityMapper.getPrimaryKey(dto);
> > > > >         Entity foundEntity = (Entity)
> > > > entityManager.find(entity.getClass(),
> > > > > pk);
> > > > >
> > > > >         if (foundEntity == null) {
> > > > >             foundEntity = entity;
> > > > >         }
> > > > >         return entityMapper.toEntity(foundEntity, dto);
> > > > >     }
> > > > >
> > > > >
> > > > >
> > > > > One could always create their own base class overriding the
> protected
> > > > > methods and adding that fetch helper I guess...
> > > > >
> > > > > cheers
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > > On 13 June 2014 12:54, Thomas Hug <thomas....@gmail.com> wrote:
> > > > >
> > > > > > Hey Karl
> > > > > >
> > > > > > Sorry missed your mail indeed - it's probably time I subscribe to
> > the
> > > > > user
> > > > > > mailing list too :-)
> > > > > >
> > > > > > You can still chain those mappers together, but I agree the
> casting
> > > and
> > > > > > wiring is clunky. As options I see:
> > > > > > - we add a public E toEntity(Dto dto) back which basically does
> the
> > > > same
> > > > > as
> > > > > > mapParameter now (just hides the casting) for new entities
> > > > > > - we make the E toEntity(Entity e, Dto dto) public as well, so
> it's
> > > > quite
> > > > > > easy to chain mapper calls for updates
> > > > > >
> > > > > > groupMapper.toEntity(user.getGroup(), userDto.getGroup());
> > > > > >
> > > > > > You will still have to have some conditionals to see which one to
> > > call,
> > > > > > also depends on your data model (required relations, lazy or
> eager
> > > > > fetch).
> > > > > > How does that look? Better ideas?
> > > > > >
> > > > > >
> > > > > >
> > > > > > On Fri, Jun 13, 2014 at 8:42 AM, Karl Kildén <
> > karl.kil...@gmail.com>
> > > > > > wrote:
> > > > > >
> > > > > > > I wrote a response to the users list but not sure it came
> > through.
> > > It
> > > > > > kinda
> > > > > > > belongs in this thread so here it goes.
> > > > > > >
> > > > > > >
> > > > > > > So I ran into issues with the DTO mapper api and voiced my
> > concerns
> > > > in
> > > > > > irc.
> > > > > > > I saw this discussion and now I am trying the solution present
> in
> > > the
> > > > > > > current SNAPSHOT. However I have one comment / question:
> > > > > > >
> > > > > > > What if my Entity has a relationship to another Entity?
> > > > > > >
> > > > > > > Like this:
> > > > > > >
> > > > > > > public class User extends BaseAuditEntity {
> > > > > > >
> > > > > > >     @Column
> > > > > > >     private String name;
> > > > > > >
> > > > > > >     @Column
> > > > > > >     @ManyToOne
> > > > > > >     @JoinColumn(name="group_id")
> > > > > > >     private Group group;
> > > > > > >
> > > > > > > This means my userDTO may come with a GroupDTO and how do I map
> > > that
> > > > > > > relationship? Or to explain by code please fill in the question
> > > marks
> > > > > > below
> > > > > > > ;) or if you feel my implementation is weird then I would
> gladly
> > > > accept
> > > > > > > comments on that too. But the way I see it I need to @Inject
> the
> > > > > > > GroupMapper, use the EntityManager to find the Group then call
> > > > > > > groupMapper.toEntity and then I think to myself that the API is
> > > worse
> > > > > now
> > > > > > > because before I could call groupMapper.toEntity with only a
> dto
> > > > > > argument.
> > > > > > > At that point you had to use the entitymanager anyways to find
> > > > > "yourself"
> > > > > > > and that feels clean compared to find your entities you form
> > > > > > relationships
> > > > > > > with.
> > > > > > >
> > > > > > >     @Override
> > > > > > >     protected User toEntity(final User user, final UserDTO
> > > userDTO) {
> > > > > > >         MapperUtil.toAuditEntity(user, userDTO);
> > > > > > >         user.setName(userDTO.getName());
> > > > > > >         user.setEmail(userDTO.getEmail());
> > > > > > >         user.setLocale(userDTO.getLocale());
> > > > > > >         user.setGroup(*?????*);
> > > > > > >         return user;
> > > > > > >     }
> > > > > > >
> > > > > > > Cheers / Karl
> > > > > > >
> > > > > > >
> > > > > > > On 17 May 2014 16:40, Romain Manni-Bucau <
> rmannibu...@gmail.com>
> > > > > wrote:
> > > > > > >
> > > > > > > > Yep, missed it.
> > > > > > > >
> > > > > > > > Works for me. Maybe the name should be closer to other
> methods,
> > > > > > > > mapKey? But whatever you choose as name this solution works
> :).
> > > > > > > >
> > > > > > > >
> > > > > > > > Romain Manni-Bucau
> > > > > > > > Twitter: @rmannibucau
> > > > > > > > Blog: http://rmannibucau.wordpress.com/
> > > > > > > > LinkedIn: http://fr.linkedin.com/in/rmannibucau
> > > > > > > > Github: https://github.com/rmannibucau
> > > > > > > >
> > > > > > > >
> > > > > > > > 2014-05-17 15:54 GMT+02:00 Thomas Hug <thomas....@gmail.com
> >:
> > > > > > > > > It's the PK, not the Entity ;) In
> SimpleQueryInOutMapperBase,
> > > it
> > > > > > could
> > > > > > > be
> > > > > > > > > something like
> > > > > > > > >
> > > > > > > > > @Inject
> > > > > > > > > private QueryInvocationContext context;
> > > > > > > > >
> > > > > > > > > protected abstract Object getPrimaryKey(Dto dto);
> > > > > > > > >
> > > > > > > > > protected E findEntity(Object pk)
> > > > > > > > > {
> > > > > > > > >     return (E)
> > > > > > > context.getEntityManager().find(context.getEntityClass(),
> > > > > > > > > pk);
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > @Override
> > > > > > > > > public Object mapParameter(final Object parameter)
> > > > > > > > > {
> > > > > > > > >     Object pk = getPrimaryKey((Dto) parameter);
> > > > > > > > >     if (pk != null)
> > > > > > > > >     {
> > > > > > > > >         E entity = findEntity(pk);
> > > > > > > > >         return toEntity(entity, (Dto) parameter);
> > > > > > > > >     }
> > > > > > > > >     return toEntity(newEntity(), (Dto) parameter);
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > On Sat, May 17, 2014 at 1:57 PM, Romain Manni-Bucau
> > > > > > > > > <rmannibu...@gmail.com>wrote:
> > > > > > > > >
> > > > > > > > >> would work while return is <E> and not Object ;)
> > > > > > > > >>
> > > > > > > > >>
> > > > > > > > >> Romain Manni-Bucau
> > > > > > > > >> Twitter: @rmannibucau
> > > > > > > > >> Blog: http://rmannibucau.wordpress.com/
> > > > > > > > >> LinkedIn: http://fr.linkedin.com/in/rmannibucau
> > > > > > > > >> Github: https://github.com/rmannibucau
> > > > > > > > >>
> > > > > > > > >>
> > > > > > > > >> 2014-05-17 11:47 GMT+02:00 Thomas Hug <
> thomas....@gmail.com
> > >:
> > > > > > > > >> > Or a protected abstract Object getPrimaryKey(Dto dto).
> We
> > > can
> > > > > get
> > > > > > > the
> > > > > > > > EM
> > > > > > > > >> > over an injected QueryInvocationContext.
> > > > > > > > >> >
> > > > > > > > >> >
> > > > > > > > >> > On Thu, May 15, 2014 at 9:06 PM, Romain Manni-Bucau
> > > > > > > > >> > <rmannibu...@gmail.com>wrote:
> > > > > > > > >> >
> > > > > > > > >> >> I think a protected findEntity(id) in the mapper can be
> > > > enough.
> > > > > > > > >> >>
> > > > > > > > >> >>
> > > > > > > > >> >> Romain Manni-Bucau
> > > > > > > > >> >> Twitter: @rmannibucau
> > > > > > > > >> >> Blog: http://rmannibucau.wordpress.com/
> > > > > > > > >> >> LinkedIn: http://fr.linkedin.com/in/rmannibucau
> > > > > > > > >> >> Github: https://github.com/rmannibucau
> > > > > > > > >> >>
> > > > > > > > >> >>
> > > > > > > > >> >> 2014-05-07 22:29 GMT+02:00 Thomas Hug <
> > > thomas....@gmail.com
> > > > >:
> > > > > > > > >> >> > Hi Romain,
> > > > > > > > >> >> > See your point. But if we only get the DTO - with
> what
> > > > would
> > > > > we
> > > > > > > > call
> > > > > > > > >> the
> > > > > > > > >> >> > find? Could even be that the PK is a DTO or encoded /
> > > > > encrypted
> > > > > > > and
> > > > > > > > >> needs
> > > > > > > > >> >> > to go through the mapper first. Maybe we can provide
> > some
> > > > > > > > convenience
> > > > > > > > >> >> > methods in the base mapper?
> > > > > > > > >> >> >
> > > > > > > > >> >> >
> > > > > > > > >> >> > On Tue, May 6, 2014 at 7:41 PM, Romain Manni-Bucau <
> > > > > > > > >> >> rmannibu...@gmail.com>wrote:
> > > > > > > > >> >> >
> > > > > > > > >> >> >> Hi guys,
> > > > > > > > >> >> >>
> > > > > > > > >> >> >> DTO feature is awesome but doesn't work in update
> mode
> > > > since
> > > > > > > isNew
> > > > > > > > >> >> >> doesn't use a managed entity.
> > > > > > > > >> >> >>
> > > > > > > > >> >> >> When using a mapper we should call find and pass it
> to
> > > the
> > > > > > > mapper
> > > > > > > > (or
> > > > > > > > >> >> >> create a new unmanaged entity if not found). So
> mapper
> > > > > > signature
> > > > > > > > >> >> >> should be Entity toEntity(Entity, DTO) no?
> > > > > > > > >> >> >>
> > > > > > > > >> >> >> Otherwise users need to do the find in the
> > > mapper...almost
> > > > > > > > eveytime.
> > > > > > > > >> >> >>
> > > > > > > > >> >> >> wdyt?
> > > > > > > > >> >> >>
> > > > > > > > >> >> >>
> > > > > > > > >> >> >> Romain Manni-Bucau
> > > > > > > > >> >> >> Twitter: @rmannibucau
> > > > > > > > >> >> >> Blog: http://rmannibucau.wordpress.com/
> > > > > > > > >> >> >> LinkedIn: http://fr.linkedin.com/in/rmannibucau
> > > > > > > > >> >> >> Github: https://github.com/rmannibucau
> > > > > > > > >> >> >>
> > > > > > > > >> >>
> > > > > > > > >>
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >
>

Reply via email to