Thursday, December 15, 2011

Entities - JPA 2.0

The JPA 2.0 specification allows us to define entities via annotations and/or xml files.  For ease of explanation, I will use annotations.  I will not discuss the sml files in this tutorial.

Lets begin by displaying the first of three entities that we have, PartEntity:

package com.sample.entities;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
 * @author Thomas Dias
 */
@Entity
@Table(name="Parts")
public class PartEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name="name", unique=true)
    private String name;
    @ManyToOne
    @JoinColumn(name = "parentId")
    private PartEntity parent;
    @OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL})
    private List<PartEntity> subParts = new ArrayList<PartEntity>();
    public PartEntity() {
    }
    @SuppressWarnings("LeakingThisInConstructor")
    public PartEntity(String name, PartEntity parent) {
        this.name = name;
        this.parent = parent;
        subParts = new ArrayList<PartEntity>();
        if(parent != null) {
            parent.getSubParts().add(this);
        }
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }
    @Override
    public boolean equals(Object object) {
        if (object==null || !(object instanceof PartEntity)) {
            return false;
        }
        if (this == object) {
            return true;
        }
        PartEntity other = (PartEntity) object;
        if (id == null || other.id == null) {
            return false;
        }
        return this.id.equals(other.id);
    }
    @Override
    public String toString() {
        return name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public PartEntity getParent() {
        return parent;
    }
    public void setParent(PartEntity parent) {
        this.parent = parent;
    }
    public List<PartEntity> getSubParts() {
        return subParts;
    }
    public void setSubParts(List<PartEntity> subParts) {
        this.subParts = subParts;
    }
}
The standard package for JPA 2.0 is javax.persistence.  If you have a namespace conflict with some annotation, be sure you are choosing this package for your JPA annotations.

The first annotation we see is @Entity.  This is what the EntityManager will look for when determining what classes it manages.  This defines this class as an Entity.  So what is an Entity?  The definition given by Wikipedia is: "A persistence entity is a lightweight Java class whose state is typically persisted to a table in a relational database."  Now, of course we don't have to be in a relational database, but lets not bother with those discussions.  The point is that we are tying the state of a java class to records in the database.

The second annotation is @Table.  If we did not include this annotation, the default behavior would kick in, and the table name would be partentity.  Many of the annotations or lack of annotations have default behavior.  Make sure you understand the defaults or your configuration will not turn out the way you expected.  I personally pluralize my tables, so I gave the name of Parts to my PartEntity class.

The next line is not an annotation, but, we need to note that the class implements Serializable.  All entities must be Serializable.

All entities must have a primary key.  This key may be annotated with @Id or with @EmbeddedId.  In this tutorial we do not use @EmbeddedId so I leave that to you to look up.  A primary key must be unique to the database table, so we need something that will make sure the Id stays unique.  Once the record is stored, you cannot change the primary key.  You would have to remove and then add the record.  This is one reason why the use of Surrogate keys are recommended.  Even keys you think are immutable do not always qualify.  I.e. did you know a person's social security number can change?  A method of ensuring primary keys are always unique is to let the EntityManager manage primary keys.  We do this by the annotation of @GeneratedValue(strategy = GenerationType.AUTO). 

The next line is the field declaration.  Here we notice three things.  What is the field name in the table?  Well, the default column name is the name of the field.  So, in this case it will be "id".  In the next annotation we see that we define the name of the column.  Although it is the same as the default, so it wasn't actually necessary.  We also did not declare the database type, only the java type.  The database type will be mapped by a default type for the java type, in some cases you can override the default type.  And the last thing we notice is where we placed the annotations relative to the java field.  We could have placed the annotation at the field or at the getter method for the field.  (Getter and setter methods as defined in EJB 1.0 and are used throughout JPA, EJB, JSF, and this tutorial.  For a given field defined by: String name, the getter method is defined by: public String getName(); and the setter method is defined by: public void setName(String name); )  Where we place the annotations define the "access type".  If we place them at the field then the access type is field, and if we place them at the getters, the access type is method.  We can mix and match access types with different entities, but WE MUST BE CONSISTENT within an entity.  Behavior is undefined if we have both an annoation for a field, and an annotation for a method even if those are two different columns.  The reason is simple, once you define your access type, the EntityManager will start interpreting its defaults.  So if you declare one field to be a column, all fields will be columns.  If you do not want a field to be a column, you must declare it to be transient.  If you declare a method to be a column, all get methods will be columns.  If you do not have a field associated with the get method, you mark it as transient.  You can see where annotating both could upset the apple cart.

The next field is name.  We have defined this to be a unique name throughout the system.  i.e. No two parts may have the same name.  Since this is unique, why wouldn't we make this the primary key?  One simple reason is what if the name changes.  Well, what if the part's name will never be changed?  What if the part name is entered incorrectly?  The concept of natural vs surrogate fields is almost a religious argument, suffice it to say, I fall on the surrogate side.  You will never be wrong if you use surrogate keys, you may be wrong if you don't.

Next we define a parent.  Parts may have sub parts.  And by definition a sub part has a parent part.  Therefor, we have to define a relationship.  The ManyToOne relationship is the other half of the OneToMany relationship.  In this case we are defining what part is the parent of this part.  Instead of letting the system define the name, we are going to chose a column name that makes sense to us.  We do that by using @JoinColumn(name = "parentId"). 


Next we define that this part may have sub parts.  We use a standard java collection.  We define the relationship by the @OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL}) annotation.  We also tell the EntityManager to cascade changes.  This means when we make a change to a child, and persist the parent, all the children will be persisted.  There are complications to this, related to stateless beans, be we will discuss that later.  In our annotation, we used the mappedBy property.  This property tells the system how we know what parts are children of this part.  The field "parent" holds the object that has the corresponding ManyToOne relationship, and thus the definition of what part is the child.  Note: this field has no column in the parts table.  It is a collection derived by a select on the primary key of this instance.  i.e. the sql statement: "select * from parts where parentid=5" would find all children of the part whose id=5.

Next we have a default constructor.  All entities MUST have a default constructor.

The next annotation @SuppressWarnings is a compiler annotation, not a JPA annotation.  Throughout this tutorial you will see various annotations.  Each annotation is defined by an import statement just as any other class is.


We override hashcode because we override equals.  We override equals because we need to know that one part of id=5 is equal to another part whose id=5.  But remember, we are allowing the entity manager to handle ids.  That means, when we create a part, no id is assigned.  It actually is not assigned until we persist the entity.  How do we know if these entities are equal?  See code for the answer.

Next we define toString to give us a string representation of this instance.

Lastly we have the ... which is to simply say all the standard getters and setters are defined.  Each field has them defined.

The other three entities defined by this tutorial do not have any new items for discussion, so I will post them here only for your review, but add no discussion regarding them.

OrderEntity.java

package com.sample.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="Orders")
public class OrderEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private int counter;
    @OneToMany(mappedBy = "orderEntity", cascade = {CascadeType.ALL})
    private List<OrderPartsEntity> orderedParts = new ArrayList<OrderPartsEntity>();
    public Long getId() {
        return id;
    }
     protected void setId(Long id) {
        this.id = id;
    }
     @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }
     @Override
    public boolean equals(Object object) {
        if (object==null || !(object instanceof OrderEntity)) {
            return false;
        }
        if (this == object) {
            return true;
        }
        OrderEntity other = (OrderEntity) object;
        if (id == null || other.id == null) {
            return false;
        }
        return this.id.equals(other.id);
    }
    @Override
    public String toString() {
        return name + " : " + id;
    }
     public String getName() {
        return name;
    }
     public void setName(String name) {
        this.name = name;
    }
     public int getCounter() {
        return counter;
    }
     public void setCounter(int counter) {
        this.counter = counter;
    }
     public List<OrderPartsEntity> getOrderedParts() {
        return orderedParts;
    }
     public void setOrderedParts(List<OrderPartsEntity> orderedParts) {
        this.orderedParts = orderedParts;
    }
 }

OrderPartsEntity.java


package com.sample.entities;
 import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
 @Entity
@Table(name="OrderParts")
public class OrderPartsEntity implements Serializable {
     private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToOne
    @JoinColumn(name = "orderId")
    private OrderEntity orderEntity;
    @OneToOne
    @JoinColumn(name = "partId")
    private PartEntity part;
    @OneToOne
    @JoinColumn(name = "orderPartsId")
    private OrderPartsEntity parent;
    @OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL})
    private List<OrderPartsEntity> subOrderParts = new ArrayList<OrderPartsEntity>();
    public OrderPartsEntity() {
    }
     @SuppressWarnings("LeakingThisInConstructor")
    public OrderPartsEntity(OrderEntity orderEntity, PartEntity part, OrderPartsEntity parent) {
        this.orderEntity = orderEntity;
        this.part = part;
        this.parent = parent;
        if (parent != null) {
            parent.getSubOrderParts().add(this);
        }
        orderEntity.getOrderedParts().add(this);
    }
     public Long getId() {
        return id;
    }
     public void setId(Long id) {
        this.id = id;
    }
     @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }
   @Override
    public boolean equals(Object object) {
        if (object==null || !(object instanceof OrderPartsEntity)) {
            return false;
        }
        if (this == object) {
            return true;
        }
        OrderPartsEntity other = (OrderPartsEntity) object;
        if (id == null || other.id == null) {
            return false;
        }
        return this.id.equals(other.id);
    }
    @Override
    public String toString() {
        return this.part.getName();
    }
    public OrderEntity getOrderEntity() {
        return orderEntity;
    }
     public void setOrderEntity(OrderEntity orderEntity) {
        this.orderEntity = orderEntity;
    }
    public OrderPartsEntity getParent() {
        return parent;
    }
    public void setParent(OrderPartsEntity parent) {
        this.parent = parent;
    }
    public PartEntity getPart() {
        return part;
    }
    public void setPart(PartEntity part) {
        this.part = part;
    }

    public List<OrderPartsEntity> getSubOrderParts() {
        return subOrderParts;
    }
    public void setSubOrderParts(List<OrderPartsEntity> subOrderParts) {
        this.subOrderParts = subOrderParts;
    }
}



UserEntity

package com.sample.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="Users")
public class UserEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(name="name", unique=true)
    private String name;
    @Column(name="password")
    private String password;
    private int counter;
    public UserEntity() {
    }
    public UserEntity(String name, String password) {
        this.name = name;
        this.password = password;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }
    @Override
    public boolean equals(Object object) {
        if (object==null || !(object instanceof UserEntity)) {
            return false;
        }
        if (this == object) {
            return true;
        }
        UserEntity other = (UserEntity) object;
        if (id == null || other.id == null) {
            return false;
        }
        return this.id.equals(other.id);
    }
    @Override
    public String toString() {
        return id + ": " + name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getCounter() {
        return counter;
    }
    public void setCounter(int counter) {
        this.counter = counter;
    }
}

No comments:

Post a Comment