Using primary key classes in session facade

Discussions

EJB design: Using primary key classes in session facade

  1. Using primary key classes in session facade (10 messages)

    Hello,

    Our server has a layer of entity beans with every bean having a corresponding primary key (<TableName>PK) class.

    What do you think about using the primary key classes in session facade as parameters or return values of session beans' methods? Doesn't this expose database structure and therefore increases coupling between the clients and server's internal implementation?

    Many tables have compound primary keys and I don't want to have multiple parameters in session bean methods refering to such records. Session beans are stateless so I can't store primary keys on server side either.

    What are the common practices in this situation?

    Thank you,
    Dmitry
  2. i was thinking along the same lines for my next project
  3. Many people say that exposing primary key values breaks encapsulation, but it is just too useful in too many situations, especially web-apps. For example, suppose you want to create a "list" page, that drills down to detail pages for individual entities. Being able to put the PK value in the hyperlink is a great way to implement this:

    <a href="showDetail.jsp?key=####">Detail for Item "Blah"</a>

    I have found that a good compromise is to allow for "Stringified" primary keys. In other words, give every PK class a toString() method, and String constructor, so that the following is always true:

    pk.equals(new PrimaryKey(pk.toString()));

    This way you have a generic mechanism for converting PKs to and from Strings. This way, you can cache PK values as String rather than objects, which isolates you from changes in the database structure.
  4. I think, we can use Primary Key values in web app but not the Primary Key Class. Having the Primary Key Class in the web app will definitely increase the coupling between EJB and Web App. So, I think we can use Primary Class in Session Facade to execute Entity beans and populate those values in Value Objects to use it in Web Apps. In that way we can reduce the coupling.

    Thanks,
    Senthil.
  5. Could you explain it in more details? Does implementation of Serializable interface by Primary Key class transform it into a Value Object? If yes, then how does it reduce coupling?

    Thank you,
    Dmitry
  6. Converting primary keys to strings solves the encapsulation problem, but returned strings must be encrypted or obfuscated to make it impossible for clients to parse it. The encryption requires additional programming and processing on the server side.

    Also type safety is poor, because a client can get a "Stringified" primary key for one record and pass it to a method that expects another type of primary key. There will be no compilation time error in this case. From this perspective, Object type can be used in place of String equally well.


    Thank you,
    Dmitry
  7. Converting primary keys to strings solves the encapsulation problem, but returned strings must be encrypted or obfuscated to make it impossible for clients to parse it. The encryption requires additional programming and processing on the server side.Also type safety is poor, because a client can get a "Stringified" primary key for one record and pass it to a method that expects another type of primary key. There will be no compilation time error in this case. From this perspective, Object type can be used in place of String equally well.Thank you, Dmitry
    Could you give me an example where we use non String or non primitive type to identify the primary key(in terms of web applications)? I think in most of the user interfaces, user communicates with a string or primitive representation only. Even in the data layer(Relational) also the primary key is represented as one of the standard sql datatypes.
    Could you explain it in more details? Does implementation of Serializable interface by Primary Key class transform it into a Value Object? If yes, then how does it reduce coupling?
    We can have a Transfer Object say ItemTO, which has id(string), name(String) etc. The id is string representation(or primitive object) of the primary key which will be loaded from the facade while you load other fields of Item bean. I think in this way the web component will be independent from ejb component.

    Thanks,
    Senthil.
  8. Here is the solution that I have in mind.

    Assume there is a table Item, primary key class ItemPK and session bean ItemFacade.

    Then, new empty interface is introduced on the session layer - ItemID. This will hide all implementation details behind Item identity from the clients.

    In this case, ItemFacade could look like this:

    class ItemFacade implements SessionBean {
    ItemID create(ItemTO item);
    void update(ItemID id, ItemTO item);
    void delete(ItemID id);
    }

    ItemID interface is implemented by an internal class ItemIDImpl and the implementation can vary depending on the needs of session layer. The simplest way would be to implement it in terms of ItemPK class:

    interface ItemID extends Serializable {
    }

    class ItemIDImpl implements ItemID {
    private ItemPK pk;
    public ItemIDImpl(ItemPK pk) { this.pk = pk; }
    public ItemPK getPk() { return pk; }
    public int hashCode() { return pk.hashCode(); }
    public boolean equals(Object that) { return pk.equals(((ItemIDImpl)that).pk); }
    public String toString() { return pk.toString(); }
    }

    Session beans will presume that ItemID interface is implemented by ItemIDImpl class only. If some client implements this interface and passes the custom object into a session bean's method, then ClassCastException will be thrown.

    This approach decouples clients from internal database structure. Also, it prevents clients from creating new item identifiers or modifying identifiers received from the server.

    Let me know what you think.

    Thank you,
    Dmitry
  9. This approach decouples clients from internal database structure. Also, it prevents clients from creating new item identifiers or modifying identifiers received from the server.
    You know, it is possible to go too far in encapsulating information. By taking this approach, you are putting some pretty severe constraints on application environment. Not allowing the client create PKs could be a big problem with any kind of application that transmits data through a non-Java layer, such as a web application, web service, messaging application or anything that interacts with XML.

    It means, for example, that you need two operations to delete an object: a find operation (to get the key) and a remove operations (to delete the object).

    Allowing this work does not gain you much, either. If the client generates a bogus PK, the worst that will happen is an error message.

    If this really is the way you want to go, there are simpler ways to achieve the same result. Simply make your PK classes immutable, with a private constructor and a PKFactory that exists only on the server.
  10. You know, it is possible to go too far in encapsulating information. By taking this approach, you are putting some pretty severe constraints on application environment. Not allowing the client create PKs could be a big problem with any kind of application that transmits data through a non-Java layer, such as a web application, web service, messaging application or anything that interacts with XML.
    Another interface (e.g. SOAP) is designed for non-Java clients. Usually, such interfaces have many limitations that in my opinion shouldn't apply to Java clients. I think encrypted opaque strings is the best representation of primary keys for XML clients.
    It means, for example, that you need two operations to delete an object: a find operation (to get the key) and a remove operations (to delete the object).
    If you provide deleteByXXX method for every findByXX method, then delete by a certain criterion can be done as a single operation.
    Allowing this work does not gain you much, either. If the client generates a bogus PK, the worst that will happen is an error message.If this really is the way you want to go, there are simpler ways to achieve the same result. Simply make your PK classes immutable, with a private constructor and a PKFactory that exists only on the server.
    If a client generates a bogus PK, the worst thing that can happen is that wrong record will be deleted. Factory can hide details of primary key format but not its structure. You will have to provide some parameters to the factory, and if the primary key changes later, then this will affect clients.

    For example, if key is integer and you represent it as a plain string, then clients can pass any integers converted to strings to delete method. Additional security checks will be needed to not allow the clients deleting records that they are not authorized to access. Later if you change type of primary key to GUID, clients will have to be updated too.

    String is a concrete type that you can't extend. Having a custom interface gives you a lot of flexibility. For example, you can extend this interface from Comparable and thus give a client possibility to sort data by primary key. Also, you can implement meaningful toString method to simplify debugging and logging on client side.

    Dmitry
  11. You raise some interesting points. I am not sure I agree with you, but this seems to be an area where two reasonable people could disagree.
    Another interface (e.g. SOAP) is designed for non-Java clients. Usually, such interfaces have many limitations that in my opinion shouldn't apply to Java clients. I think encrypted opaque strings is the best representation of primary keys for XML clients.
    I am not sure I agree with the encryption part, but otherwise I agree with this.
    If you provide deleteByXXX method for every findByXX method, then delete by a certain criterion can be done as a single operation.
    That's a lot of extra work for CRUD type operations. On the other hand, you can probably handle it with generated code, so it may not be so bad.
    If a client generates a bogus PK, the worst thing that can happen is that wrong record will be deleted. Factory can hide details of primary key format but not its structure. You will have to provide some parameters to the factory, and if the primary key changes later, then this will affect clients. For example, if key is integer and you represent it as a plain string, then clients can pass any integers converted to strings to delete method. Additional security checks will be needed to not allow the clients deleting records that they are not authorized to access.
    Encrypted keys are not an adequate security mechanism. You need real security checks to prevent manipulation of data that the user is not allowed to access. For example, they could use the deleteByXXX() methods you mention above to search for records that they could delete.
    Later if you change type of primary key to GUID, clients will have to be updated too.
    If the client treats the Stringified PK simply as a String value, rather than trying to interpret its data in different ways, then changes in the PK type won't effect the client. They will simply get a String that happens to be a GUID rather than a String that happens to be an Integer, but they will still be able to retrieve/delete objects using the Stringified key.

    For that matter, it won't matter if you switched to an encrypted key rather than a plain-text key; it will still be String.
    String is a concrete type that you can't extend. Having a custom interface gives you a lot of flexibility. For example, you can extend this interface from Comparable and thus give a client possibility to sort data by primary key. Also, you can implement meaningful toString method to simplify debugging and logging on client side.
    These kinds of customizations will be locking the client into a particular implementation of the key. If you switch from Integer to GUID, the sort order will be effected, and the behavior of the client will change.

    I agree with you on the toString() method, but I think that applies equally well to my Stringified key approach.

    I have seen a lot of cases where people wrote custom classes or interfaces for Primary Keys, but I have only seen a handful of cases where these custom classes gave any value over simple Integers and Strings: combined keys (if that is the way your database is implemented, not my first choice for clean development) and the ancient FatKey pattern people used to optimized BMP EJBs.