Displaying and Using PrimeFaces Bar chart From a Database
This blog post is about displaying a bar chart from a database in a JavaServer Faces application using PrimeFaces. In the PrimeFaces user documentation, the examples use static data to produce different charts using hard coded values. There is already a CRUD Java EE application that have been developed using JavaServer Faces + Java Enterprise Beans + Java Persistence and MYSQL as the Database. The data to be visually represented and rendered in the application using the PrimeFaces Bar Chart component is to be read from this database.
The Entity Class for the application looks like this
package lifesavers.database.entity;
import java.io.Serializable;
//import statements ommitted for brevity
//import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author omozegieaziegbe
*/
@Entity
@Table(name = "customer", catalog = "lifesaverscanada", schema = "")
//@XmlRootElement
@NamedQueries({
@NamedQuery(name = "Customer.findAll", query = "SELECT c FROM Customer c")
, @NamedQuery(name = "Customer.findById", query = "SELECT c FROM Customer c WHERE c.id = :id")
, @NamedQuery(name = "Customer.findByFirstname", query = "SELECT c FROM Customer c WHERE c.firstname = :firstname")})
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id", nullable = false)
private Integer id;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 245)
@Column(name = "firstname", length = 245)
private String firstname;
private String middlename;
private String lastname;
private String primaryEmail;
private String phone;
private String address;
private String postalCode;
private String courseNumber;
@Temporal(TemporalType.DATE)
private Date courseDate;
private String courseName;
private BigDecimal price;
private BigDecimal totalWithGst;
public Customer() {
}
public Customer(Integer id) {
this.id = id;
}
public Customer(Integer id, String firstname, String middlename, String lastname, String primaryEmail, String phone, String address, String postalCode,String courseNumber, Date courseDate, String courseName, BigDecimal price, BigDecimal totalWithGst) {
this.id = id;
this.firstname = firstname;
this.middlename = middlename;
this.lastname = lastname;
this.primaryEmail = primaryEmail;
this.phone = phone;
this.address = address;
this.postalCode = postalCode;
this.courseNumber = courseNumber;
this.courseDate = courseDate;
this.courseName = courseName;
this.price = price;
this.totalWithGst = totalWithGst;
}
// Note that getters and setters, hashCode, equals and toString method have been ommitted
}
The EJB Abstract Facade contains the CRUD methods auto generated by Netbeans. Two additional methods to perform the search query to be used in the Bar chart is added. These methods, findCustomersForChart and findCustomersForBarChart uses JPQL to query for course name to be displayed on the X-axis and total amount each course has generated to be displayed on the Y-axis respectively.
package lifesavers.database.session;
import java.math.BigDecimal;
//other import statements ommitted for brevity
/**
*
* @author omozegieaziegbe
*/
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0] + 1);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
public List<String> findCustomersForChart() {
Query query = getEntityManager().createQuery("SELECT DISTINCT (c.courseName) FROM Customer c WHERE c.courseName is not NULL ORDER BY c.courseName ASC");
List<String> chartList = query.getResultList();
return chartList;
}
public List<BigDecimal> findCustomersForBarChart() {
Query query = getEntityManager().createQuery("Select SUM (c.totalWithGst) from Customer c WHERE c.courseName is not NULL GROUP BY c.courseName ORDER BY c.courseName ASC");
List<BigDecimal> chartList = query.getResultList();
return chartList;
}
}
The EJB Customer Facade auto generated by Netbeans looks like this
package lifesavers.database.session;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import lifesavers.database.entity.Customer;
/**
*
* @author omozegieaziegbe
*/
@Stateless
public class CustomerFacade extends AbstractFacade<Customer> {
@PersistenceContext(unitName = "omos.microsystems_lifesaverscanada_war_1.0-SNAPSHOTPU")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public CustomerFacade() {
super(Customer.class);
}
}
And the Bar Chart bean (ChartBean) for the application is as follows
package lifesavers.database.clientbean;
import javax.inject.Named;
import java.io.Serializable;
//other import statements ommitted for brevity
/**
*
* @author omozegieaziegbe
*/
@Named(value = "chartBean")
@RequestScoped
public class ChartBean implements Serializable {
private BarChartModel barModel;
@EJB
private CustomerFacade customerfacade;
@PostConstruct
public void init() {
createCustomerBarModel();
}
/**
* Creates a new instance of ChartBean
*/
public ChartBean() {
}
public void createCustomerBarModel() {
ChartSeries customerseries = new ChartSeries();
List<BigDecimal> customerList = customerfacade.findCustomersForBarChart();
List<String> customerChart = customerfacade.findCustomersForChart();
List<Number> customerMap = new ArrayList<>();
List<String> labels = new ArrayList<>();
//figures to be displayed on the Y-axis
customerList.forEach((final Number a) -> customerMap.add(a));
//labels to used on the X-axis
customerChart.forEach((final String a) -> labels.add(a));
barModel = new BarChartModel();
ChartData data = new ChartData();
BarChartDataSet barDataSet = new BarChartDataSet();
barDataSet.setLabel("Company Name Dataset");
barDataSet.setData(customerMap);
List<String> bgColor = new ArrayList<>();
bgColor.add("rgba(255, 99, 132, 0.2)");
barDataSet.setBackgroundColor(bgColor);
List<String> borderColor = new ArrayList<>();
borderColor.add("rgb(255, 99, 132)");
barDataSet.setBorderColor(borderColor);
barDataSet.setBorderWidth(1);
data.addChartDataSet(barDataSet);
data.setLabels(labels);
barModel.setData(data);
barModel.setExtender("extender1");
BarChartOptions options = new BarChartOptions();
CartesianScales cScales = new CartesianScales();
CartesianLinearAxes linearAxes = new CartesianLinearAxes();
linearAxes.setOffset(true);
CartesianLinearTicks ticks = new CartesianLinearTicks();
ticks.setBeginAtZero(true);
linearAxes.setTicks(ticks);
cScales.addYAxesData(linearAxes);
options.setScales(cScales);
Title title = new Title();
title.setDisplay(true);
title.setFontSize(20);
title.setText("Revenue generated grouped by course name");
options.setTitle(title);
Legend legend = new Legend();
legend.setDisplay(true);
legend.setPosition("top");
LegendLabel legendLabels = new LegendLabel();
legendLabels.setFontStyle("bold");
legendLabels.setFontColor("#9370DB");
legendLabels.setFontSize(25);
legend.setLabels(legendLabels);
options.setLegend(legend);
// disable animation
Animation animation = new Animation();
animation.setDuration(0);
options.setAnimation(animation);
barModel.setOptions(options);
}
public BarChartModel getBarModel() {
return barModel;
}
public void setBarModel(BarChartModel barModel) {
this.barModel = barModel;
}
}
Finally the JSF (Faces) page to display the bar chart component looks like this
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Company BarChart</title>
<h:outputStylesheet name="css/lifesavers.css"/>
<h:outputScript library="javax.faces" name="jsf.js" target="body" />
<script>
function extender1() {
var options = {
options: {
scales: {
yAxes: [{
scaleLabel: {
display: true,
labelString: 'Total Amount With GST'
}
}],
}
}
};
$.extend(true, this.cfg.config, options);
}
;
</script>
</h:head>
<h:body>
<h:form>
<header>
<nav class="clear">
</nav>
</header>
<div class="card" style="border: 0px; height: 800px; width:85%; margin-left: 15px">
<p:barChart model="#{chartBean.barModel}" />
</div>
</h:form>
<footer>
<p>Copyright 2021.</p>
</footer>
</h:body>
</html>
When the application is completed and built, the Bar Chart should look like the figure below:
Note: The Primefaces version used in this application is 10.0.0 and the application code for primefaces sometimes changes.