GIG

赴くままに技術を。

Hyperjaxb3なるものを使ってみる

Hyperjaxb3?

Jaxb + JPA

JaxbがXML <-> Javaオブジェクトをするものであるのに対して、Hyperjaxb3はXML <-> RDBを行うことができる。しかし、実際のところXML<->Entity(POJO) <-> RDBとしており、XML <-> Entity(POJO)はJaxbを利用している。

動かしてみる

ディレクトリ構成

(参考 : http://confluence.highsource.org/display/HJ3/Purchase+Order+Tutorial)

  • チュートリアルでは、HSQLDBを使っているが、PostgreSQLに変更した。
  • チュートリアルのサンプルのpom.xmlを見たらJUnitがコメントアウトされていた。
    • 試しにコメントアウトを外して見たら、"TestCaseが解釈されない"みたいなエラーが発生
      • JUnit 3で書かれているためか?

■pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>jp.hermensian</groupId>
    <artifactId>Hj3Demo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Hj3Demo</name>
    <dependencies>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.1-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.4-1</version>
        </dependency>
        <dependency>
            <groupId>org.jvnet.hyperjaxb3</groupId>
            <artifactId>hyperjaxb3-ejb-runtime</artifactId>
            <version>0.5.6</version>
        </dependency>

        <!-- Test dependencies -->
                 <!-- TODO : This invokes error. -->
        <!-- dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.11</version>
                        <scope>test</scope>
                 </dependency-->

        <!-- Roundtrip -->
        <dependency>
            <groupId>org.jvnet.hyperjaxb3</groupId>
            <artifactId>hyperjaxb3-ejb-roundtrip</artifactId>
            <version>0.5.6</version>
        </dependency>
        <!-- Hibernate Dependencies -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.6.5.Final</version>
            <scope>test</scope>
        </dependency>
   <!-- Postgresql JDBC Driver -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.2-1003-jdbc4</version>
    </dependency>
    </dependencies>
    <build>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.jvnet.hyperjaxb3</groupId>
                <artifactId>maven-hyperjaxb3-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <extension>true</extension>                     
                                      <roundtripTestClassName>RoundtripTest</roundtripTestClassName>
                </configuration>
            </plugin>
            <plugin>
                <inherited>true</inherited>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

■persistence.properties

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.connection.driver_class=org.postgresql.Driver
hibernate.connection.username=developer
hibernate.connection.password=developer
hibernate.connection.url= jdbc:postgresql://localhost:5432/test
hibernate.hbm2ddl.auto=create-drop
hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
hibernate.jdbc.batch_size=0

Roundtrip Test?

(参考:http://confluence.highsource.org/display/HJ3/Roundtrip+Test)

Roundtrip Testは以下のサイクルを実行している。

  1. XMLから作成したEntityとsrc/test/resources/persistence.propertiesを利用して, src/test/samplesにあるxmlファイルを全てunmarshallして、永続化する
  2. 1と異なるトランザクションを生成し、データベースからオブジェクトを取得して、今度はmarshallし、得られたxmlファイルを最初のxmlファイルと比較する

テストその1(生成したEntityからJPAを利用して永続化)

org.jvnet.hyperjaxb3.ejb.tests.po.*はHyperjaxb3によって作られたクラスである。

package hj3demo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import org.jvnet.hyperjaxb3.ejb.tests.po.ObjectFactory;
import org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType;

/**
 * XMLスキーマから作ったEntityをテストする.
 * @author hermesian
 */
public class JPATest {

    /**
     * XMLから生成した際に生成されるObjectFactoryクラス.
     */
    private ObjectFactory of;

    /**
     * EntityManagerFactory.
     */
    private EntityManagerFactory emf;

    /**
     * setup.
     * @throws Exception 
     */
    @Before
    public void setUp() throws Exception {
        
        of = new ObjectFactory();
        
        final Properties prop = new Properties();
        InputStream is = null;
        try {
            is = getClass().getClassLoader().getResourceAsStream(
                    "persistence.properties");
            prop.load(is);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ignored) {
                    
                }
            }
        }
        emf = Persistence.createEntityManagerFactory("org.jvnet.hyperjaxb3.ejb.tests.po", prop);
    }

    /**
     * XMLから生成したEntityの永続化をテストする.
     */
    @Test
    public void testSaeAndLoad() {

        // setup
        final PurchaseOrderType alpha = of.createPurchaseOrderType();
        alpha.setShipTo(of.createUSAddress());
        alpha.getShipTo().setCity("Sacramento");
   
        // exercise
        // alphaを永続化する
        final EntityManager em1 = emf.createEntityManager();
        em1.getTransaction().begin();
        em1.persist(alpha);
        em1.getTransaction().commit();
        em1.close();

        // 永続化したレコードのIDを取得する
        final Long id = alpha.getHjid();

        // verify
        final EntityManager em2 = emf.createEntityManager();
        final PurchaseOrderType beta = em2.find(PurchaseOrderType.class, id);
        em2.close();
        assertThat("Sacramento", is(beta.getShipTo().getCity()));
    }
}

テスト(その2) (Marshall, UnMarshall, Validation)

  • Marshall : JavaオブジェクトからXMLを生成すること
  • Unmarshall : XMLからJavaオブジェクトを生成すること
package hj3demo;

import java.io.File;
import java.util.LinkedList;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import org.junit.Assert;
import org.jvnet.hyperjaxb3.ejb.tests.po.ObjectFactory;
import org.jvnet.hyperjaxb3.ejb.tests.po.PurchaseOrderType;
import org.xml.sax.SAXException;

/**
 * XMLからEntityに変換したり、EntityからXMLに変換したりするテスト.
 * @author hermesian
 */
public class XMLtoEntityTest {

    /**
     * JAXBContext.
     * <per>
     * javax.xml.bind.*は、スレッドセーフじゃない
     * javax.xml.bind.JAXBContextクラスはスレッドセーフである
     * </per>
     */
    private JAXBContext context;

    /**
     * XMLから生成した際に生成されるObjectFactoryクラス.
     */
    private ObjectFactory of;

    /**
     * setup.
     * @throws Exception 
     */
    @Before
    public void setUp() throws Exception {
        // コンテキストパスは生成されたEntityのパッケージ名
        context = JAXBContext.newInstance("org.jvnet.hyperjaxb3.ejb.tests.po");

        // ヘルパークラス
        of = new ObjectFactory();
    }
 
    /**
     * src/test/samples/po.xmlをアンマーシャルした結果を比較するテスト.
     * @throws JAXBException 
     */
    @Test
    public void testUnmarshall() throws JAXBException {

        final Unmarshaller unmarshaller = context.createUnmarshaller();
        final Object object = unmarshaller.unmarshal(new File("src/test/samples/po.xml"));

        // verify
        // キャスト
        final PurchaseOrderType purchaseOrder = ((JAXBElement<PurchaseOrderType>) object).getValue();
        
        // 要素を比較
        assertThat("Mill Valley", is(purchaseOrder.getShipTo().getCity()));
    }

    /**
     * Entityをマーシャルした結果を比較するテスト.
     * @throws JAXBException
     * @throws XPathExpressionException 
     */
    @Test
    public void testMarshall() throws JAXBException, XPathExpressionException {

        // setup
        final PurchaseOrderType purchaseOrder = of.createPurchaseOrderType();
        purchaseOrder.setShipTo(of.createUSAddress());
        purchaseOrder.getShipTo().setCity("New Orleans");
        final JAXBElement<PurchaseOrderType> purchaseOrderElement = of.createPurchaseOrder(purchaseOrder);

        // exersize
        final Marshaller marshaller = context.createMarshaller();
        final DOMResult result = new DOMResult();
        marshaller.marshal(purchaseOrderElement, result);
        
        // verify
        final XPathFactory xPathFactory = XPathFactory.newInstance();
        assertThat("New Orleans", is(xPathFactory.newXPath().evaluate("/purchaseOrder/shipTo/city", result.getNode())));
    }

    /**
     * マーシャルする際にValidateするテスト.
     * @throws SAXException
     * @throws JAXBException 
     */
    @Test
    public void testValidateWhenMarshalling() throws SAXException, JAXBException {

        // setup
        final PurchaseOrderType purchaseOrder = of.createPurchaseOrderType();
        purchaseOrder.setShipTo(of.createUSAddress());
        purchaseOrder.setBillTo(of.createUSAddress());
        final JAXBElement<PurchaseOrderType> purchaseOrderElement = of.createPurchaseOrder(purchaseOrder);
        
        // XMLスキーマインスタンスを作成
        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = schemaFactory.newSchema(new StreamSource(getClass().getClassLoader().getResourceAsStream("schema.xsd")));

        // マーシャルしてみる
        final Marshaller marshaller = context.createMarshaller();
        marshaller.setSchema(schema);
        
        final List<ValidationEvent> events = new LinkedList<ValidationEvent>();
        marshaller.setEventHandler(new ValidationEventHandler() {
            @Override
            public boolean handleEvent(ValidationEvent event) {
                events.add(event);
                return true;
            }
        });
        marshaller.marshal(purchaseOrderElement, new DOMResult());

        // verify
        Assert.assertFalse("List of validation events must not be emty.", events.isEmpty());
        System.out.println("check :" + events.get(0));
    }

    /**
     * アンマーシャルする際にValidateするテスト.
     * @throws JAXBException
     * @throws SAXException 
     */
    @Test
    public void testValidateWhenUnMarshalling() throws JAXBException, SAXException {
        
        // XMLスキーマインスタンスを作成
        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        final Schema schema = schemaFactory.newSchema(new StreamSource(getClass().getClassLoader().getResourceAsStream("schema.xsd")));

        final Unmarshaller unmarshaller = context.createUnmarshaller();
        unmarshaller.setSchema(schema);

        final List<ValidationEvent> events = new LinkedList<ValidationEvent>();
        unmarshaller.setEventHandler(new ValidationEventHandler() {
            @Override
            public boolean handleEvent(ValidationEvent event) {
                events.add(event);
                return true;
            }
        });
        final Object object = unmarshaller.unmarshal(new File("src/test/samples/po.xml"));
        
        // verify
        Assert.assertTrue("List of validation events must be emty.", events.isEmpty());
    }
}

懸念事項

今のところ気になることが性能問題。 もう少し深堀してみる。