{"id":143,"date":"2011-07-07T07:10:43","date_gmt":"2011-07-07T14:10:43","guid":{"rendered":"http:\/\/sloanseaman.com\/wordpress\/?p=143"},"modified":"2011-07-07T07:11:49","modified_gmt":"2011-07-07T14:11:49","slug":"setting-on-a-set","status":"publish","type":"post","link":"http:\/\/sloanseaman.com\/wordpress\/2011\/07\/07\/setting-on-a-set\/","title":{"rendered":"Setting on a Set()"},"content":{"rendered":"<p>Maybe this post should be called &#8216;Setting on a Sunset()&#8217; but Oracle now owns Java, and &#8216;Setting on an OracleSet()&#8217; sounds programmatically correct but it just doesn&#8217;t have the same ring (or the feel of a nice drink in your hand while sitting on a beach&#8230;.. writing java ;)) <\/p>\n<p>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.<\/p>\n<p>For instance, say you have two objects (using JPA Annotations):<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Entity\r\n@Table(name=&quot;USER&quot;)\r\npublic class User {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @ManyToOne(fetch=FetchType.LAZY)\r\n   @JoinColumn(name = &quot;COMPANY_ID&quot;, referencedColumnName = &quot;ID&quot;, nullable = false)\r\n   private Company company;\r\n   \r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Company getCompany() { return company; }\r\n   public void setCompany(Company company) { this.company = company }\r\n}\r\n\r\n@Entity\r\n@Table(name=&quot;COMPANY&quot;)\r\npublic class Company {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;company&quot;, \r\n          cascade = { CascadeType.MERGE, CascadeType.PERSIST })\r\n   private Set&lt;User&gt; users = new HashSet&lt;User&gt;(0);\r\n\r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Set&lt;User&gt; getUsers() { return users; }\r\n   public void setUsers(Set&lt;User&gt; users) { this.users = users; }\r\n}\r\n<\/pre>\n<p>This is a pretty straightforward, standard way of making a 1-Many relationship in an ORM (my experience is with Hibernate mainly).  <\/p>\n<p>Side note:<br \/>\nMy 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&#8217;m just suggesting that you put some thought into it \ud83d\ude09<\/p>\n<p>So lets start using our objects and set some things:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n... some method definition...\r\nCompany company = new Company();\r\ncompany.setName(&quot;World Wide Wicket&quot;);\r\n\r\nUser user = new User();\r\nuser.setName(&quot;Sloan&quot;);\r\nuser.setCompany(company);\r\ncompany.addUser(user);\r\n.... more code ...\r\n<\/pre>\n<p>Doesn&#8217;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&#8217;t it be better to just call one method to accomplish this?  Shouldn&#8217;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)?<\/p>\n<p>So I was thinking about this and I came up with a few different ways.  I&#8217;ll go over them and also what is good\/bad about them as well as any other pitfalls I have identified.<\/p>\n<h3>Custom Set implementation<\/h3>\n<p>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:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class Company {\r\n@Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;company&quot;, \r\n          cascade = { CascadeType.MERGE, CascadeType.PERSIST })\r\n   private Set&lt;User&gt; users = new UserHashSet&lt;User&gt;(this);\r\n\r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Set&lt;User&gt; getUsers() { return users; }\r\n   public void setUsers(Set&lt;User&gt; users) { this.users = users; }\r\n\r\n   public class UserHashSet extends HashSet&lt;User&gt; {\r\n      private Company company;\r\n\r\n      public UserHashSet(Company company) {\r\n         this.company = company;\r\n      }\r\n\r\n      public void add(User user) {\r\n         super.add(user);\r\n         user.setCompany(company);\r\n      }\r\n\r\n      ... do the same for addAll ...\r\n   }\r\n}\r\n<\/pre>\n<h4>Pros:<\/h4>\n<ul>\n<li>The code is pretty straight forward for the developers when adding:\n<\/li>\n<\/ul>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ncompany.getUsers().add(user);\r\n<\/pre>\n<h4>Cons:<\/h4>\n<ul>\n<li>You have to code a whole other class<\/li>\n<li>If someone calls company.setUsers() they can override your UserHashSet and add() will no longer work correctly<\/li>\n<li>Developers, if playing it safe, would still probable do:<\/li>\n<\/ul>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ncompany.getUsers().add(user);\r\nuser.setCompany(company);\r\n<\/pre>\n<p>So, as slick as this may be in some ways, I don&#8217;t think it&#8217;s the way to go because it allows the developers to still do additional work as well as really screw things up.<\/p>\n<h3>Code in set() and an add() method<\/h3>\n<p>Another option is to put some code in the User.setCompany() and create a method in the Company called addUser().  Something like:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Entity\r\n@Table(name=&quot;USER&quot;)\r\npublic class User {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @ManyToOne(fetch=FetchType.LAZY)\r\n   @JoinColumn(name = &quot;COMPANY_ID&quot;, referencedColumnName = &quot;ID&quot;, nullable = false)\r\n   private Company company;\r\n   \r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Company getCompany() { return company; }\r\n   public void setCompany(Company company) { \r\n      this.company = company \r\n      company.getUsers().add(this);\r\n   }\r\n}\r\n\r\n@Entity\r\n@Table(name=&quot;COMPANY&quot;)\r\npublic class Company {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;company&quot;, \r\n          cascade = { CascadeType.MERGE, CascadeType.PERSIST })\r\n   private Set&lt;User&gt; users = new HashSet&lt;User&gt;(0);\r\n\r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Set&lt;User&gt; getUsers() { return users; }\r\n   public void setUsers(Set&lt;User&gt; users) { this.users = users; }\r\n\r\n   public void addUser(User user) {\r\n      user.setCompany(user);\r\n   }\r\n}\r\n<\/pre>\n<h4>Pros:<\/h4>\n<ul>\n<li>Less code than the previous example<\/li>\n<li>A little more direct in having the developer use it.  They can do it either way and it will work<\/li>\n<\/ul>\n<h4>Cons:<\/h4>\n<ul>\n<li>Still allows developers to go around the addUser() method when adding a user.  They can still call setUsers() and getUsers().add()<\/li>\n<\/ul>\n<p>We could try solving it by doing:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n   ... company object code ...\r\n   public Set&lt;User&gt; getUsers() {\r\n      return Collections.unmodifiableSet(users);\r\n   }\r\n\r\n   void setUsers(Set&lt;User&gt; users) {\r\n      this.users = users;\r\n   }\r\n\r\n   public void addUser(User user) {\r\n      user.setCompany(user);\r\n   }\r\n<\/pre>\n<p>This would restrict the developer form using company.getUsers().add() and also hide the setUsers() from them.<br \/>\nThe issue though is now User.setCompany() cannot call company.getUsers().add() so we have broken the User code.<\/p>\n<h3>The Best Solution?<\/h3>\n<p>Let me state that I don&#8217;t know if I have the best solution for this.  This is what I came up with.<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Entity\r\n@Table(name=&quot;USER&quot;)\r\npublic class User {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @ManyToOne(fetch=FetchType.LAZY)\r\n   @JoinColumn(name = &quot;COMPANY_ID&quot;, referencedColumnName = &quot;ID&quot;, nullable = false)\r\n   private Company company;\r\n   \r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Company getCompany() { return company; }\r\n   public void setCompany(Company company) { \r\n      this.company = company \r\n      company.addUser(this);\r\n   }\r\n}\r\n\r\n@Entity\r\n@Table(name=&quot;COMPANY&quot;)\r\npublic class Company {\r\n   @Id\r\n   @Column(name=&quot;ID&quot;)\r\n   private Long id;\r\n\r\n   @Column(name=&quot;NAME&quot;)\r\n   private String name;\r\n\r\n   @OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;company&quot;, \r\n          cascade = { CascadeType.MERGE, CascadeType.PERSIST })\r\n   private Set&lt;User&gt; users = new HashSet&lt;User&gt;(0);\r\n\r\n   public Long getId() { return id; }\r\n   public void setId(Long id) { this.id = id; }\r\n\r\n   public String getName() { return name; }\r\n   public void setName(String name) { this.name = name; }\r\n\r\n   public Set&lt;User&gt; getUsers() { \r\n      return Collections.unmodifiableSet(users); \r\n   }\r\n   void setUsers(Set&lt;User&gt; users) { this.users = users; }\r\n\r\n   void addUser(User user) {\r\n      users.add(user);\r\n   }\r\n<\/pre>\n<h4>Pros:<\/h4>\n<ul>\n<li>This limits the developer to setting the Company\/User relationship in only one way that we can control and set the relationship with<\/li>\n<li>Less code than the first example, more controlled than the second<\/li>\n<\/ul>\n<h4>Cons:<\/h4>\n<ul>\n<li>Developers may be confused that they can only set the Company on the user object<\/li>\n<\/ul>\n<p>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.<\/p>\n<p>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 \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Maybe this post should be called &#8216;Setting on a Sunset()&#8217; but Oracle now owns Java, and &#8216;Setting on an OracleSet()&#8217; sounds programmatically correct but it just doesn&#8217;t have the same ring (or the feel of a nice drink in your &hellip; <a href=\"http:\/\/sloanseaman.com\/wordpress\/2011\/07\/07\/setting-on-a-set\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7,10],"tags":[29],"_links":{"self":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/143"}],"collection":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/comments?post=143"}],"version-history":[{"count":12,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/143\/revisions"}],"predecessor-version":[{"id":155,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/posts\/143\/revisions\/155"}],"wp:attachment":[{"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/media?parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/categories?post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/sloanseaman.com\/wordpress\/wp-json\/wp\/v2\/tags?post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}