Setting on a Set()

Maybe this post should be called ‘Setting on a Sunset()’ but Oracle now owns Java, and ‘Setting on an OracleSet()’ sounds programmatically correct but it just doesn’t have the same ring (or the feel of a nice drink in your hand while sitting on a beach….. writing java ;))

Anyway, there are many times when dealing with an ORM that you might want to call a setter on an object that exists in a 1-Many relationship and have that set() method do some other work.

For instance, say you have two objects (using JPA Annotations):

@Entity
@Table(name="USER")
public class User {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @ManyToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "COMPANY_ID", referencedColumnName = "ID", nullable = false)
   private Company company;
   
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Company getCompany() { return company; }
   public void setCompany(Company company) { this.company = company }
}

@Entity
@Table(name="COMPANY")
public class Company {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "company", 
          cascade = { CascadeType.MERGE, CascadeType.PERSIST })
   private Set<User> users = new HashSet<User>(0);

   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Set<User> getUsers() { return users; }
   public void setUsers(Set<User> users) { this.users = users; }
}

This is a pretty straightforward, standard way of making a 1-Many relationship in an ORM (my experience is with Hibernate mainly).

Side note:
My HashSet was initially set to a size of 0. If a Collection has the possibility of staying empty you should initialize it to 0 because otherwise some implementation will immediate make it 10 spaces and you will waste memory. However, initializing it to 0 also means that we take a small performance hit when we first put something into it since it has to allocate space. I’m just suggesting that you put some thought into it 😉

So lets start using our objects and set some things:

... some method definition...
Company company = new Company();
company.setName("World Wide Wicket");

User user = new User();
user.setName("Sloan");
user.setCompany(company);
company.addUser(user);
.... more code ...

Doesn’t that seem a bit annoying? You set the Company in the User but then you have to add the User to the Companies list of Users. Wouldn’t it be better to just call one method to accomplish this? Shouldn’t you just have to set the Company in the User and that be it (or just add the User to the list of Users in the Company)?

So I was thinking about this and I came up with a few different ways. I’ll go over them and also what is good/bad about them as well as any other pitfalls I have identified.

Custom Set implementation

The first thing I thought was: Well, I can create my own collection so that when it is called it will do things for me. That would look something like:

public class Company {
@Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "company", 
          cascade = { CascadeType.MERGE, CascadeType.PERSIST })
   private Set<User> users = new UserHashSet<User>(this);

   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Set<User> getUsers() { return users; }
   public void setUsers(Set<User> users) { this.users = users; }

   public class UserHashSet extends HashSet<User> {
      private Company company;

      public UserHashSet(Company company) {
         this.company = company;
      }

      public void add(User user) {
         super.add(user);
         user.setCompany(company);
      }

      ... do the same for addAll ...
   }
}

Pros:

  • The code is pretty straight forward for the developers when adding:
company.getUsers().add(user);

Cons:

  • You have to code a whole other class
  • If someone calls company.setUsers() they can override your UserHashSet and add() will no longer work correctly
  • Developers, if playing it safe, would still probable do:
company.getUsers().add(user);
user.setCompany(company);

So, as slick as this may be in some ways, I don’t think it’s the way to go because it allows the developers to still do additional work as well as really screw things up.

Code in set() and an add() method

Another option is to put some code in the User.setCompany() and create a method in the Company called addUser(). Something like:

@Entity
@Table(name="USER")
public class User {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @ManyToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "COMPANY_ID", referencedColumnName = "ID", nullable = false)
   private Company company;
   
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Company getCompany() { return company; }
   public void setCompany(Company company) { 
      this.company = company 
      company.getUsers().add(this);
   }
}

@Entity
@Table(name="COMPANY")
public class Company {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "company", 
          cascade = { CascadeType.MERGE, CascadeType.PERSIST })
   private Set<User> users = new HashSet<User>(0);

   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Set<User> getUsers() { return users; }
   public void setUsers(Set<User> users) { this.users = users; }

   public void addUser(User user) {
      user.setCompany(user);
   }
}

Pros:

  • Less code than the previous example
  • A little more direct in having the developer use it. They can do it either way and it will work

Cons:

  • Still allows developers to go around the addUser() method when adding a user. They can still call setUsers() and getUsers().add()

We could try solving it by doing:

   ... company object code ...
   public Set<User> getUsers() {
      return Collections.unmodifiableSet(users);
   }

   void setUsers(Set<User> users) {
      this.users = users;
   }

   public void addUser(User user) {
      user.setCompany(user);
   }

This would restrict the developer form using company.getUsers().add() and also hide the setUsers() from them.
The issue though is now User.setCompany() cannot call company.getUsers().add() so we have broken the User code.

The Best Solution?

Let me state that I don’t know if I have the best solution for this. This is what I came up with.

@Entity
@Table(name="USER")
public class User {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @ManyToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "COMPANY_ID", referencedColumnName = "ID", nullable = false)
   private Company company;
   
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Company getCompany() { return company; }
   public void setCompany(Company company) { 
      this.company = company 
      company.addUser(this);
   }
}

@Entity
@Table(name="COMPANY")
public class Company {
   @Id
   @Column(name="ID")
   private Long id;

   @Column(name="NAME")
   private String name;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "company", 
          cascade = { CascadeType.MERGE, CascadeType.PERSIST })
   private Set<User> users = new HashSet<User>(0);

   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public Set<User> getUsers() { 
      return Collections.unmodifiableSet(users); 
   }
   void setUsers(Set<User> users) { this.users = users; }

   void addUser(User user) {
      users.add(user);
   }

Pros:

  • This limits the developer to setting the Company/User relationship in only one way that we can control and set the relationship with
  • Less code than the first example, more controlled than the second

Cons:

  • Developers may be confused that they can only set the Company on the user object

So, it sounds like a simple thing but really I had a put a bit of thought into it. The main issue is that you need to control how the developer will do things and keep it constricted so that they are forced down a specific path so that you can manipulate the objects correctly.

If you think, or know, there is a better solution than this, please post it! No one said I was always going to get it right 🙂

About sseaman

Connect with me on Google+
This entry was posted in Java, Programming and tagged . Bookmark the permalink.

5 Responses to Setting on a Set()

  1. Srinivas says:

    Thanks for the nice article.

    I was reading an article over JPA implementations earlier and was looking at this article which i wanted to share with you. Let me know your thoughts.

    http://blog.xebia.com/2009/05/jpa-implementation-patterns-service-facades-and-data-transfers-objects/

    • sseaman says:

      Let me first state that I’m all for little objects. In my opinion if you have many objects that are over 1000 lines, you may be doing something wrong.

      That being said, here is my opinion on a few of the points from that article:
      DAO
      I think DAO’s still have their place. Yes, you can do much with the EntityManager, but what about NamedQueries? What about a find method that requires data manipulation first?

      DTO
      I’m also a fan of DTO’s.

      My current project is a Spring backend with a Flex front end where we pass the actual ORM objects up to Flex. One department codes the Flex and another codes the Java. This means that if the Java developers decide to re architect some of the underlying model, the flex code has to be changed.
      With a DTO in the middle, the DTO could be coded to still have the same functionality that the flex code demands while hiding the changes that were made to the Java code till a point in time where the flex code could be changed to reflect the java changes.

      Without something like a DTO a simple method name change can become nightmarish because you know you broke the flex code, but you don’t know where so you have to make a separate team aware of the minor change and not deploy the change till they have the time to change their code.

      The downside of the DTO is the creation of more objects which are just for data transfers, as well as the performance hit with the creation and population of the DTO objects.

      The upside is the avoidance of what I described above as well as the ability to change the resulting objects type depending on request type (as described in the article).

      Service Facade
      I think the need of a Service Facade depends on the needs of the application. If you are going to have mutliple client types (AMF, SOAP, JSON(REST)) then I think a Service Facade is a requirement. Since the business logic will be the same on the server and the return object type is the only thing that is changing.
      Also, this structure already exists in many architectures (STRUTS 2.0 for sure, and SPRING MVC I would think).

      Think about it:
      Client calls app
      Service Facade
      App calls business logic
      DTO is returned
      Service Facade determines call type and converts DTO to correct response type.

      You end up with business logic that is basically straight Spring and can easily be unit tested while still supporting mulitple client types.

      I like it.

  2. Srinivas C says:

    Thanks for your inputs Sloan.
    c
    Please clarify, if you have a Service Facade, will it scale for medium to big applications where there are multiple modules with multiple functionalities.

    The problem is that, at some point in time with a growing application Service Facade will become a monolith structure and changing any single function of a module would require the whole service facade to be atleast regression tested.

    Do you use one controller for each functional module for your current project or what is pattern which is advisable for a controller?

    • sseaman says:

      A few points: Any time you place a single point of entry for multiple objects/execution points/etc you risk heavy regression testing if you make a change. So yes, if you tweaked the object it would require a full test. The way to minimize this is to modularize the facade so that it perhaps follows a delegator model where, depended on the return type, different classes that implement a specific interface can process the code. This way, if you need to change the way JSON is handled, you only change the class relating to JSON.

      In the current application you could say we use a Service Facade. All of our services return native objects which are then handled by BlazeDS which then converts them to AMF to send up to flex. An upgrade to BlazeDB would require (though I know most companies wouldn’t do it) full regression testing.

      So, will it scale? Yes, it should. It’s basically an MVC architecture and that has existed for some time and is proven.

Leave a Reply

Your email address will not be published. Required fields are marked *