Discussions

EJB design: Entity EJB and primary key in DTO

  1. Entity EJB and primary key in DTO (9 messages)

    Hi all,

    if I use an entity bean I can't change value of the primary key field. I suppose it's the desired behaviour of EJBs. So I think it's not a good design to use the name field of the modelled object as a primary key because it can change during it's lifetime (for example a customer can marry). It may be better to generate an unique ID every time I create an EJB.

    My first question is that is this right what I wrote.

    If it's right then comes my problem. If I use Data Objects for data flow between the client side and the server side (some kind of facade), which is the better design from the followings:

    1. In my DTO the setID method is public, which means that I have to handle the problem of duplicate keys on the server side (in case the client modified the primary key).

    2. The setID has private access, so the client can't modify the primary key. In this case how can you manage it with XDoclet? (by default XDoclet generates public setters for each field in a DTO).

    Thx in advance.

    Regards, Ivan

    Threaded Messages (9)

  2. Entity EJB and primary key in DTO[ Go to top ]

    Hi all,if I use an entity bean I can't change value of the primary key field... My first question is that is this right what I wrote.

    This is my understanding. When learning EJBs, I attempted to create an EJB with a mutable key. It fail miserably.
    which is the better?

    The first option might be okay if you trust the EJB client implicitly. Likewise, you could simply make the assumption that the immutable property (ID in this case) is only valid when you sent it to the client and never trust what the client sends back. Given that you'll probably need the field to use as a key, though, this might not be the best way to go.

    The second option might work. However, you can't make setID private. If you do, the only class that could call setID would be the DTO itself. In my case, I've made my setters (all of them, since my DTOs have been read-only) protected. That way the EJB that creates/populates them has access to do so (the DTO and EJB are in the same package), but the EJB client can only read information out of them (they are in a separate package).

    I'm afraid I won't be much help with XDoclet, as I have never used it.
  3. Entity EJB and primary key in DTO[ Go to top ]

    Thanks for help, it seems a good solution (private access was a wrong idea, as you pointed it out).
    I've another problem. So if I use an id as a primary key, but I have a field (for example NAME) which I want to be unique, how can I manage it?
    I create in the table a unique index for that field.
    1. I call create a second EJB with the same name.
    2. The create method returns.
    3. But when ejbStore is called, it throws a SQLException.

    How can I catch this exception? I'm afraid, there is no way to catch this on the server side.
    Should I try to catch it on the client side? Or should I manage it in the facade (for example try to find an EJB with the same name, and if it returns a notnull object, throw an exception)?
    I hope you understand my question.

    Ivan
  4. Entity EJB and primary key in DTO[ Go to top ]

    What do you mean you can't catch the exception? Who calls the YourEjbHome.create() method?
    If you have a facade, feel free to do it. Wrap the SQLException within some other application relative exception, so the client catches this rather than SQLException.

    Cheers and happy coding,
    Martin
  5. Entity EJB and primary key in DTO[ Go to top ]

    I CAN'T! REALLY! :)
    I have a facade which creates a new bean but with an already existing Name (Name must be unique). In the facade's newBean method (which is called by the client) I call the home interface's create() method. It looked something like this (I already have delete it):

    public void newBean(BeanDTO data){
      try {
        BeanLocalHome home = getLocalHome();//getting home
        home.create(data);
      } catch (CreateException e){
        //handling code
      } catch (Exception e){//this should catch all kind of exception

      }
      System.out.prinln("No Exception");//the program gets here
    }

    So if in the create method somthing nasty occurs, it s sure that the second catch catches it. But it doesn't! The sysout call in the end is executed and the method returns. After that the server drops an error, some TransactionRolledBack exception.
    As I tried to find out the problem, I've put some debugging code into it. And it showed me, that the ejbStore method (which I think actually throws the exception) is called after the create method returns and the newBean() method has finished. So the newBean() method is executed proprely, but the transaction ends after the newBean() method returns, and than is the exception thrown.
    Since yesterday I wrote a method in the facade which examines whether the name already exists in the table with different pk, and if yes, it throws an exception. It's a soolution, but I'd like to handle it in the bean itself.

    Thanks, Ivan
  6. Entity EJB and primary key in DTO[ Go to top ]

    Of course there was exception handling in the second catch, but I was lazy to write it in the reply.
    Ivan
  7. Entity EJB and primary key in DTO[ Go to top ]

    It's me again. :)
    Finally I've found a solution, I simply call ejbStore from the bean's ejbPostCreate method and it immediately throws an exception which I can catch. What do you think about this? It seems to me safe.

    Ivan
  8. Entity EJB and primary key in DTO[ Go to top ]

    It is NOT safe, in fact! You should not call lifecicle methods by yourself. They're meant to be called by the lifecicle manager... that is the container.
    What method in the EJB performs the INSERT in the DB? Please check this is done within ejbCreate. If a constraint violation exists, a SQLException occurs, and the container will propagate it up the call stack to the facade. It'd be a nice idea to catch it in the ejbCreate, though.
    My guess is that you're performing both INSERT and UPDATE in ejbStore, deciding if the object has ever been INSERTed through a flag. This approach is wrong... for the reasons you've so empirically discovered.... ;)

    Cheers and happy coding,
    Martin
  9. Entity EJB and primary key in DTO[ Go to top ]

    Thanks, Martin, you're right, I've checked it and I shuoldn't call ejbStore directly.
    My bean is CMP, so there is no code written by me which inserts or updates the table, the container generates it. The create method only sets the fields, and ejbStore is an empty method.
    I've put it back to the facade. But I've another great idea (:))). What if I call some method from the create method, which do the same thing as the facade? I mean I call a selectByName, if it returns an EJB, than I throw an error. Anyway, this doesn't solve my problem with updates. If I call setName(), it can happen that I would violate the unique constraint, but the method returns, and if ejbStore is called, the exception is thrown. As far as I know in CMP setXXX methods must be abstract so I can't write a similar thing as in the create method. A solution can be that I don't declare set methods in the interface of the EJB, but some update methods, which then call set methods in the bean after a check. But I think it's too complicated to waste time with it.
    What do you think?
    Ivan
  10. Entity EJB and primary key in DTO[ Go to top ]

    I think you're suffering the good old "model impedance" syndromme... ;)
    In order your DB constraint to work, the UPDATE should be performed when you entity.setName("THIS ONE IS DUPLICATED").
    If this happened, performance would go down the drain.
    So, your problem is: your desing is kindda data-driven. And I say this because you created the constraint in the DB and now you find yourself trying to workaround this in your object model. I don't want to raise the old war here, anyway; just making a point.
    I whould not recommend to the home.findByName("") approach. If an entity actually exists for the given name, you'd get it loaded, which is undesirable for oh so many many reasons. Perhaps you could change the philosophy of the finder, changing it for a home method nameAlreadyExists(String) which returns a boolean; or even better, a nameIsValid(String) throwing an exception.
    Perhaps CMP is no good for THAT entity.

    Let me know if you come around a nice solution.

    Cheers and happy coding,
    Martin