Liferay Jpa Portlet

A typical issue in liferay is a portlet which is able to show data at one portal site. In this article I’d like to illustrate an easy portlet that manage an hardware warehouse.

I used Spring framework and eclipseLink implementation of Jpa. I found out them a very useful combination that work fine togheter.

It has been a long time since I wrote my last article. Now, I’m working on Liferay portal 6.1 Community Edition and a real first example, you would be wonder about that, is a single portlet that expose the typical CRUD operation (Create, Read, Update, Delete) on two tables.

I used MySQL with Liferay deployed on Tomcat 7 but, as you well know, that doesn’t matter for the aim of this article.

Let’s start with the Database Diagram:

liferaywarehouse

And the Sql Script for the Order table:

CREATE TABLE `order_tbl` (
  `order_Id` int(11) NOT NULL,
  `description` varchar(20) NOT NULL,
  PRIMARY KEY (`order_Id`)
)

And the Product table:

CREATE TABLE `product_tbl` (
  `product_Id` varchar(20) NOT NULL,
  `description` varchar(100) NOT NULL,
  `price` double NOT NULL,
  `qtaAvailable` int(11) NOT NULL,
  `order_id` int(11) NOT NULL,
  PRIMARY KEY (`product_Id`),
  KEY `IX_Order` (`order_id`),
  CONSTRAINT `IX_Order` FOREIGN KEY (`order_id`) REFERENCES `order_tbl` (`order_Id`)
)

As I previously said, I used Spring framework combined with Portlet definition. It’s not aim of this article describe the portlet standard definition and the use of it in Liferay. You can find a lot of resources in internet that speak about it very deeper way.

The Entities from the above schema are the follows:


package it.techannotation.warehouse.jpa;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity(name="warehouseorder")
@Table(name="order_tbl")
public class Order {

@Id
@Column(name="order_id", nullable = false)
private int order_id;

@Column(name="description", nullable = false)
private String description;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "order", cascade={CascadeType.PERSIST, CascadeType.REMOVE})
private Set products = new HashSet(0);

public Order(){}

public Order(int order_id)
{
this.order_id = order_id;
}

public Order(int order_id, String description)
{
this.order_id = order_id;
this.description = description;
}

public int getOrder_id() {
return order_id;
}

public void setOrder_id(int order_id) {
this.order_id = order_id;
}

public Set getProducts() {
return products;
}

public void setProducts(Set products) {
this.products = products;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

}

And Product:

package it.techannotation.warehouse.jpa;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity(name = "warehouseproduct")
@Table(name = "product_tbl")
public class Product {

@Id
@Column(name = "product_id", nullable = false)
private String product_id;

@Column(name = "description", nullable = false)
private String description;

@Column(name = "price", nullable = false)
private double price;

@Column(name = "qtaAvailable", nullable = false)
private double qtaAvailable;

@ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.PERSIST})
@JoinColumn(name = "order_id", nullable = false)
private Order order;

public Product() {
}

public Product(String product_id, String description, double price,
double qtaAvailable) {

this.product_id = product_id;
this.description = description;
this.price = price;
this.qtaAvailable = qtaAvailable;
}

public String getProduct_id() {
return product_id;
}

public void setProduct_id(String product_id) {
this.product_id = product_id;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

public double getQtaAvailable() {
return qtaAvailable;
}

public void setQtaAvailable(double qtaAvailable) {
this.qtaAvailable = qtaAvailable;
}

public Order getOrder() {
return order;
}

public void setOrder(Order order) {
this.order = order;
}

}

Take a look at the highlight rows where I built the one-to-many relation between the tables Order and Product. I’d like to take some time to go deeper into this argument but, as I said before, I might go out of context.

Now the service layer. First the Order table.

package it.techannotation.warehouse.dao;

import it.techannotation.warehouse.jpa.Order;

import java.util.List;

public interface OrderDao {
public List getOrders();
public void insertOrder(Order order);
public void deleteOrder(int orderId);
}

And its implementation

package it.techannotation.warehouse.dao;

import it.techannotation.warehouse.jpa.Order;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository("OrderDao")
public class EclipseLinkOrderDao implements OrderDao {

@PersistenceContext
private EntityManager em;

@SuppressWarnings("unchecked")
@Override
public List getOrders() {

Query query = em.createQuery("SELECT o FROM warehouseorder o",
Order.class);
query.setHint("javax.persistence.cache.storeMode", "REFRESH");

List orders = query.getResultList();

return orders;
}

@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertOrder(Order order) {
em.persist(order);

}

@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteOrder(int orderId) {
Order order = em.find(Order.class, orderId);

em.remove(order);

}
}

At the highlight row I put the cache eviction for refreshing the data into the dataset.

The Product table Dao.

package it.techannotation.warehouse.dao;

import it.techannotation.warehouse.jpa.Product;
import java.util.List;

public interface ProductDao {
public List getProducts();
public void insertProduct(int orderId, Product product);
public void deleteProduct(String productId);
}

The implementation

package it.techannotation.warehouse.dao;

import it.techannotation.warehouse.jpa.Order;
import it.techannotation.warehouse.jpa.Product;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository("ProductDao")
public class EclipseLinkProductDao implements ProductDao {

@PersistenceContext
private EntityManager em;

@SuppressWarnings("unchecked")
@Override
public List getProducts() {

Query query = em.createQuery("SELECT p FROM warehouseproduct p",
Order.class);
query.setHint("javax.persistence.cache.storeMode", "REFRESH");

return query.getResultList();
}

@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void insertProduct(int orderId, Product product) {
Order order = em.find(Order.class, orderId);

product.setOrder(order);

order.getProducts().add(product);
em.persist(order);

}

@Override
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void deleteProduct(String productId) {

Product product  = em.find(Product.class, productId);

em.remove(product);

em.getEntityManagerFactory().getCache().evict(Order.class);

}
}

At last the serviceDao


package it.techannotation.warehouse.service;

import java.util.List;

import it.techannotation.warehouse.dao.OrderDao;
import it.techannotation.warehouse.jpa.Order;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("orderService")
public class OrderService {

@Autowired
@Qualifier("OrderDao")
private OrderDao orderDao;

public List getOrders() {
return orderDao.getOrders();
}

public void insertOrder(Order order) {
orderDao.insertOrder(order);
}

public void deleteOrder(int orderId) {
orderDao.deleteOrder(orderId);
}

}

package it.techannotation.warehouse.service;

import java.util.List;

import it.techannotation.warehouse.dao.ProductDao;
import it.techannotation.warehouse.jpa.Product;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("productService")
public class ProductService {

@Autowired
@Qualifier("ProductDao")
private ProductDao productDao;

public List getProducts() {
return productDao.getProducts();
}

public void insertProduct(int orderId, Product product) {
productDao.insertProduct(orderId, product);
}

public void deleteProduct(String productId) {
productDao.deleteProduct(productId);
}

}

To avoid a too long article I’ve taken the decision to split it into two parts. In the second part we see the portlet part in Liferay that consume the data layer.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s