Monday, January 11, 2016

Coherence backed by JPA with Java Client

We are using Order Entry Schema thats come as sample schema with Oracle Database Installation. For this blog, I have been using

  • Oracle 12c Database
  • Eclipse Mars
  • Coherence 12c 12.2.1

Here is the major steps for implementation

  • Creating JPA Persistence Unit and Entities
  • Create the Cache Configuration File for JPA
  • Create a Java Client to Interact with Data Object


Step 1 Creating JPA Persistence Unit and Entities

The first step in implementing JPA is creating entities & persistent configuration. I am assuming that you have created the necessary JPA project & added the required libraries for  creating the entities.

I am using Order, Order Entry & Product Information tables from the OE schema. The below is the corresponding java entities.

Orders.java

/**
 * The persistent class for the ORDERS database table.
 * 
 */
@Entity
@Table(name="ORDERS")
@NamedQuery(name="Order.findAll", query="SELECT o FROM Order o")
public class Order implements Serializable, PortableObject {
private static final long serialVersionUID = 1L;

@Id

@Column(name="ORDER_ID")
private long orderId;

@Column(name="CUSTOMER_ID")

private long customerId;

//@Column(name="ORDER_DATE")

//private Timestamp orderDate;

@Column(name="ORDER_MODE")

private String orderMode;

@Column(name="ORDER_STATUS")

private Float orderStatus;

@Column(name="ORDER_TOTAL")

private Float orderTotal;

@Column(name="PROMOTION_ID")

private long promotionId;

@Column(name="SALES_REP_ID")

private long salesRepId;

//bi-directional many-to-one association to OrderItem

@OneToMany(mappedBy="order", fetch=FetchType.EAGER)
private List<OrderItem> orderItems;

public Order() {

}



public List<OrderItem> getOrderItems() {

return this.orderItems;
}

public void setOrderItems(List<OrderItem> orderItems) {

this.orderItems = orderItems;
}

public OrderItem addOrderItem(OrderItem orderItem) {

getOrderItems().add(orderItem);
orderItem.setOrder(this);

return orderItem;

}

public OrderItem removeOrderItem(OrderItem orderItem) {

getOrderItems().remove(orderItem);
orderItem.setOrder(null);

return orderItem;

}

@Override

public void readExternal(PofReader in) throws IOException {
this.orderId = in.readLong(0);
this.customerId = in.readLong(1);
this.orderMode = in.readString(2);
this.orderStatus = in.readFloat(3);
this.orderTotal = in.readFloat(4);
this.promotionId = in.readLong(5);
this.salesRepId = in.readLong(6);
this.orderItems = in.readCollection(7, this.orderItems);
//this.orderDate = in.readObject(8);
}

@Override

public void writeExternal(PofWriter out) throws IOException {
out.writeLong(0, this.orderId);
out.writeFloat(1, this.customerId);
out.writeString(2, this.orderMode);
out.writeFloat(3, this.orderStatus);
out.writeFloat(4, this.orderTotal);
out.writeLong(5, this.promotionId);
out.writeLong(6, this.salesRepId);
out.writeCollection(7, this.orderItems);
//out.writeObject(8, this.orderDate);
}

}


OrderItem.Java

@Entity
@Table(name="ORDER_ITEMS")
@NamedQuery(name="OrderItem.findAll", query="SELECT o FROM OrderItem o")
public class OrderItem implements Serializable, PortableObject {
private static final long serialVersionUID = 1L;

@EmbeddedId

private OrderItemPK id;

@Column(name="QUANTITY")

private long quantity;

@Column(name="UNIT_PRICE")

private long unitPrice;

//bi-directional many-to-one association to Order

@ManyToOne
@JoinColumn(name="ORDER_ID")
private Order order;

//bi-directional many-to-one association to ProductInformation

@ManyToOne
@JoinColumn(name="PRODUCT_ID")
private ProductInformation productInformation;

public OrderItem() {

}

public OrderItemPK getId() {

return this.id;
}


public Order getOrder() {
return this.order;
}

public void setOrder(Order order) {

this.order = order;
}

public ProductInformation getProductInformation() {

return this.productInformation;
}

public void setProductInformation(ProductInformation productInformation) {

this.productInformation = productInformation;
}

@Override

public void readExternal(PofReader in) throws IOException {
this.quantity = in.readLong(0);
this.unitPrice = in.readLong(1);
this.productInformation = in.readObject(2);
this.id = in.readObject(3);
this.order = in.readObject(4);
//this.order = in.readObject(4);
}

@Override

public String toString() {
return "OrderItem [id=" + id + ", quantity=" + quantity + ", unitPrice=" + unitPrice + ", order=" + order
+ ", productInformation=" + productInformation + "]";
}

@Override

public void writeExternal(PofWriter out) throws IOException {
out.writeLong(0, this.quantity);
out.writeLong(1, this.unitPrice);
out.writeObject(2, this.productInformation);
out.writeObject(3, this.id);
out.writeObject(4, this.order);
}

}


OrderItemPK.java

package entities;

import java.io.IOException;

import java.io.Serializable;
import javax.persistence.*;

import com.tangosol.io.pof.PofReader;

import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;

/**

 * The primary key class for the ORDER_ITEMS database table.
 * 
 */
@Embeddable
public class OrderItemPK implements Serializable, PortableObject {
@Override
public String toString() {
return "OrderItemPK [orderId=" + orderId + ", lineItemId=" + lineItemId + "]";
}

//default serial version id, required for serializable classes.

private static final long serialVersionUID = 1L;

@Column(name="ORDER_ID", insertable=false, updatable=false)

private long orderId;

@Column(name="LINE_ITEM_ID")

private long lineItemId;

public OrderItemPK() {

}
public long getOrderId() {
return this.orderId;
}
public void setOrderId(long orderId) {
this.orderId = orderId;
}
public long getLineItemId() {
return this.lineItemId;
}
public void setLineItemId(long lineItemId) {
this.lineItemId = lineItemId;
}

public boolean equals(Object other) {

if (this == other) {
return true;
}
if (!(other instanceof OrderItemPK)) {
return false;
}
OrderItemPK castOther = (OrderItemPK)other;
return (this.orderId == castOther.orderId);
}

public int hashCode() {

//System.out.print("PK hashCode");
final int prime = 31;
int hash = 17;
hash = hash * prime + ((int) (this.orderId ^ (this.orderId >>> 32)));
hash = hash * prime + ((int) (this.lineItemId ^ (this.lineItemId >>> 32)));

return hash;
}

@Override
public void readExternal(PofReader in) throws IOException {
this.orderId = in.readLong(0);
this.lineItemId = in.readLong(1);
}

@Override

public void writeExternal(PofWriter out) throws IOException {
out.writeLong(0, this.orderId);
out.writeLong(1, this.lineItemId);
}
//@Override
public int compareTo(Object o) {
OrderItemPK i = (OrderItemPK) o;
    if(this.getOrderId() < i.getOrderId())
        return -1;

    if(this.getOrderId() > i.getOrderId())

        return 1;

    return 0;

}
}

ProductInformation.java

@Entity
@Table(name="PRODUCT_INFORMATION")
@NamedQuery(name="ProductInformation.findAll", query="SELECT p FROM ProductInformation p")
public class ProductInformation implements Serializable, PortableObject {

private static final long serialVersionUID = 1L;

@Id

@Column(name="PRODUCT_ID")
private long productId;

@Column(name="CATALOG_URL")

private String catalogUrl;

@Column(name="CATEGORY_ID")

private int categoryId;

@Column(name="LIST_PRICE")

private float listPrice;

@Column(name="MIN_PRICE")

private float minPrice;

@Column(name="PRODUCT_DESCRIPTION")

private String productDescription;

@Column(name="PRODUCT_NAME")

private String productName;

@Column(name="PRODUCT_STATUS")

private String productStatus;

@Column(name="SUPPLIER_ID")

private long supplierId;

@Column(name="WEIGHT_CLASS")

private int weightClass;

//bi-directional many-to-one association to OrderItem

@OneToMany(mappedBy="productInformation")
private List<OrderItem> orderItems;

public ProductInformation() {

}

public List<OrderItem> getOrderItems() {

return this.orderItems;
}

public void setOrderItems(List<OrderItem> orderItems) {

this.orderItems = orderItems;
}

public OrderItem addOrderItem(OrderItem orderItem) {

getOrderItems().add(orderItem);
orderItem.setProductInformation(this);

return orderItem;

}

public OrderItem removeOrderItem(OrderItem orderItem) {

getOrderItems().remove(orderItem);
orderItem.setProductInformation(null);
return orderItem;
}

@Override

public void readExternal(PofReader in) throws IOException {
this.productId = in.readLong(0);
this.catalogUrl = in.readString(1);
this.categoryId = in.readInt(2);
this.listPrice = in.readFloat(3);
this.minPrice = in.readFloat(4);
this.productDescription = in.readString(5);
this.productName = in.readString(6);
this.productStatus = in.readString(7);
this.supplierId = in.readLong(8);
this.weightClass = in.readInt(9);
}

@Override

public void writeExternal(PofWriter out) throws IOException {
out.writeLong(0, this.productId);
out.writeString(1, this.catalogUrl);
out.writeInt(2, this.categoryId);
out.writeFloat(3, this.listPrice);
out.writeFloat(4, this.minPrice);
out.writeString(5, this.productDescription);
out.writeString(6, this.productName);
out.writeString(7, this.productStatus);
out.writeLong(8, this.supplierId);
out.writeInt(9, this.weightClass);
}

}


persistence.java

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="OrderEntryPrj" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>entities.Order</class>
<class>entities.OrderItem</class>
<class>entities.OrderItemPK</class>
<class>entities.ProductInformation</class>
<properties>
<property name="eclipselink.target-server" value="None"/>
<property name="javax.persistence.jdbc.url" value="zzz:1521:orcl"/>
<property name="javax.persistence.jdbc.user" value="oe"/>
<property name="javax.persistence.jdbc.password" value="oracle"/>
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
      <property name="eclipselink.logging.timestamp" value="false"/>
      <property name="eclipselink.logging.thread" value="false"/>
      <property name="eclipselink.logging.session" value="false"/>
      <property name="eclipselink.logging.connection" value="false"/>
      <property name="eclipselink.logging.exceptions" value="true"/>
      <property name="eclipselink.cache.shared.default" value="false"/>
     
</properties>
</persistence-unit>
</persistence>


2. Create the Cache Configuration File for JPA

The 2nd step is to create coherence config file.

<cache-config>
  <caching-scheme-mapping>
    <cache-mapping>
      <!-- Set the name of the cache to be the entity name.  -->
      <cache-name>Order</cache-name>
      <!-- Configure this cache to use the following defined scheme. -->
      <scheme-name>jpa-distributed</scheme-name>
    </cache-mapping>
    <cache-mapping>
      <!-- Set the name of the cache to be the entity name.  -->
      <cache-name>OrderItem</cache-name>
      <!-- Configure this cache to use the following defined scheme. -->
      <scheme-name>jpa-distributed</scheme-name>
    </cache-mapping>
  </caching-scheme-mapping>
  <caching-schemes>
    <distributed-scheme>
      <scheme-name>jpa-distributed</scheme-name>
      <service-name>JpaDistributedCache</service-name>
       <serializer>
      <instance>
      <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
      <init-params>
        <init-param>
          <param-type>String</param-type>
          <param-value>pof-config.xml</param-value>
        </init-param>
      </init-params>
      </instance>
    </serializer>
      <backing-map-scheme>
        <read-write-backing-map-scheme>
          <internal-cache-scheme>
            <local-scheme/>
          </internal-cache-scheme>
          <!-- Define the cache scheme. -->
          <cachestore-scheme>
            <class-scheme>
              <class-name>
                com.tangosol.coherence.jpa.JpaCacheStore
              </class-name>
              <init-params>

                <!-- This param is the entity name. -->

                <init-param>
                  <param-type>java.lang.String</param-type>
                  <param-value>{cache-name}</param-value>
                </init-param>

                <!-- This param is the fully qualified entity class. -->

                <init-param>
                 <param-type>java.lang.String</param-type>
                  <param-value>entities.{cache-name}</param-value>
                </init-param>

                <!-- This param should match the value of the -->

                <!-- persistence unit name in persistence.xml. -->
                <init-param>
                  <param-type>java.lang.String</param-type>
                  <param-value>OrderEntryPrj</param-value>
                </init-param>
              </init-params>
            </class-scheme>
          </cachestore-scheme>
        </read-write-backing-map-scheme>
      </backing-map-scheme>
    <autostart>true</autostart>
    </distributed-scheme>
<!-- The below is required for extendes client. For this example, this can be removed -->
  <proxy-scheme>
      <service-name>ExtendTcpProxyService</service-name>
      <acceptor-config>
        <tcp-acceptor>
          <local-address>
            <address>localhost</address>
            <port>9099</port>
          </local-address>
        </tcp-acceptor>
        <serializer>
          <instance><class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name></instance>
        </serializer>
      </acceptor-config>
      <autostart>true</autostart>
    </proxy-scheme>
  </caching-schemes>
</cache-config>


3. Create a Java Client to Interact with Data Object
package client;

public class OrderClient {

public static void main(String[] args) {

System.out.println("Accessing Coherence Cache from Java - Order Client");
Scanner in = new Scanner(System.in);
OrderItem item = null;
System.out.print("Enter Order ID to fetch: ");
String orderId = in.nextLine();
NamedCache<Object, Object> orderCache = CacheFactory.getCache("Order");


Order order1 = (Order) orderCache.get(Long.parseLong(orderId));
System.out.println("[Order ID: " + order1.getOrderId() + ", Order Mode: " + order1.getOrderMode()
+ ", Order Total: $" + order1.getOrderTotal() + ", Products Ordered = {");
int size = order1.getOrderItems().size();
for (int i = 0; i < size; i++) {
item = order1.getOrderItems().get(i);
System.out.println("ItemID: " + item.getId().getLineItemId() + ", Qty: " + item.getQuantity() + ", Product Name: " + item.getProductInformation().getProductName() + "#");
}
System.out.println("}");

order1.setOrderMode("online");
orderCache.put(orderId, order1);
Order order2 = (Order) orderCache.get(orderId);
System.out.println("Order Status:" + order2.getOrderStatus() + "<->Order Mode:" + order2.getOrderMode()
+ "<->:Order Item Size: " + order1.getOrderItems().size());

}


}



PS: Both Client & Server are using same coherence config in this example.









No comments: