2013年9月7日星期六

NetBeans_021:开发JavaEE 7 应用之四:使用Batch计算售票总金额

开发运行环境:NetBeans7.3.1。

关于Batch 1.0,请参考《JavaEE7 十大新特性》。

1. SalesReader.java
SalesReader.java是ItemReader,每次读取sales.csv的一行数据。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.glassfish.movieplex7.batch;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import javax.batch.api.chunk.AbstractItemReader;
import javax.enterprise.context.Dependent;
import javax.inject.Named;

/**
 *
 * @author pmma
 */
@Named
@Dependent
public class SalesReader extends AbstractItemReader {

    private BufferedReader reader;

    @Override
    public void open(Serializable checkpoint) throws Exception {
        reader = new BufferedReader(
                new InputStreamReader(
                Thread.currentThread()
                .getContextClassLoader()
                .getResourceAsStream("META-INF/sales.csv")));
    }

    @Override
    public String readItem() {
        String string = null;
        try {
            string = reader.readLine();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return string;
    }
}

2. SalesProcessor.java
SalesProcessor.java是ItemProcessor,每次分析读取SalesReader提供的Item数据,即读取的sales.csv的一行数据,把电影id和金额信息保存到Sales Entity中。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.glassfish.movieplex7.batch;

import java.util.StringTokenizer;
import javax.batch.api.chunk.ItemProcessor;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
import org.glassfish.movieplex7.entities.Sales;

/**
 *
 * @author pmma
 */
@Named
@Dependent
public class SalesProcessor implements ItemProcessor {

    @Override
    public Sales processItem(Object s) {
        Sales sales = new Sales();
        StringTokenizer tokens = new StringTokenizer((String) s, ",");
        sales.setId(Integer.parseInt(tokens.nextToken()));
        sales.setAmount(Float.parseFloat(tokens.nextToken()));
        return sales;
    }
}

3. SalesWriter.java
SalesWriter.java是ItemWriter,批量提交多个每次分析读取SalesProcessor提供的Item数据,即把多个Sales Entities一次提交到数据库中。


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.glassfish.movieplex7.batch;

import java.util.List;
import javax.batch.api.chunk.AbstractItemWriter;
import javax.enterprise.context.Dependent;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import org.glassfish.movieplex7.entities.Sales;

/**
 *
 * @author pmma
 */
@Named
@Dependent
public class SalesWriter extends AbstractItemWriter {

    @PersistenceContext
    EntityManager em;

    @Override
    @Transactional
    public void writeItems(List list) {
        for (Sales s : (List) list) {
            em.persist(s);
        }
    }
}
说明:
(1)在writeItems上有 @Transactional批注,说明该方法是带事务的,这是JPA 2.1的新特性,即支持在任意一个POJO类上加事务。

4. Job XML配置文件:eod-sales.xml
<job id="endOfDaySales" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     version="1.0">
    <step id="populateSales">
        <chunk item-count="3" skip-limit="5">
            <reader ref="salesReader"/>
            <processor ref="salesProcessor"/>
            <writer ref="salesWriter"/>
            <skippable-exception-classes>
                <include class="java.lang.NumberFormatException"/>
            </skippable-exception-classes>
        </chunk>
    </step>
</job>
说明:
(1)endOfDaySales Job有一个Step,该Step有一个Chunk。
(2)在Chunk中,item-count="3",在这里表明一次处理3行销售数据。
(3)skippable-exception-classes:java.lang.NumberFormatException表明忽略该异常。
(4)skip-limit="5",表明最多忽略5次异常。

5. SalesBean.java
SalesBean.java是一个Managed Bean,负责启动Job,其中使用了JobOperator。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.glassfish.movieplex7.batch;

import java.util.List;
import java.util.Properties;
import javax.batch.operations.JobOperator;
import javax.batch.operations.JobStartException;
import javax.batch.runtime.BatchRuntime;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import org.glassfish.movieplex7.entities.Sales;

/**
 *
 * @author pmma
 */
@Named
@RequestScoped
public class SalesBean {

    @PersistenceUnit
    EntityManagerFactory emf;

    public void runJob() {
        try {
            JobOperator jo = BatchRuntime.getJobOperator();
            long jobId = jo.start("eod-sales", new Properties());
            System.out.println("Started job: with id: " + jobId);
        } catch (JobStartException ex) {
            ex.printStackTrace();
        }
    }

    public List getSalesData() {
        return emf.createEntityManager().createNamedQuery("Sales.findAll",
                Sales.class).getResultList();
    }

}

6. sales.xhtml
Table绑定#{salesBean.salesData},用于显示电影售票情况;按钮绑定#{salesBean.runJob()},点击启动Job。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <body>

        <ui:composition template="./../WEB-INF/template.xhtml">

            <ui:define name="content">
                <h1>Movie Sales</h1>
                <h:form>
                    <h:dataTable value="#{salesBean.salesData}" var="s" border="1">
                        <h:column>
                            <f:facet name="header">
                                <h:outputText value="Show ID" />
                            </f:facet>
                            #{s.id}
                        </h:column>
                        <h:column>
                            <f:facet name="header">
                                <h:outputText value="Sales" />
                            </f:facet>
                            #{s.amount}
                        </h:column>
                    </h:dataTable>
                    <h:commandButton value="Run Job" action="sales"
                                     actionListener="#{salesBean.runJob()}"/>
                    <h:commandButton value="Refresh" action="sales" />
                </h:form>
            </ui:define>

        </ui:composition>

    </body>
</html>

Project 下载:5.movieplex7(Batch).7z

没有评论: