Hyperjaxb3?
Jaxb + JPA
JaxbがXML <-> Javaオブジェクトをするものであるのに対して、Hyperjaxb3はXML <-> RDBを行うことができる。しかし、実際のところXML<->Entity(POJO) <-> RDBとしており、XML <-> Entity(POJO)はJaxbを利用している。
動かしてみる
ディレクトリ構成
- src/main/resources/schema.xsd
- xmlスキーマはPurchase Order Schema Example のpo.xsdを持ってきている
- src/test/java/hj3demo/JPATest.java
- xmlから作成したEntityを使って永続化をテスト
- src/test/java/hj3demo/XMLtoEntityTest.java
- jaxbの機能(marshall, unmarshall, validation)テスト
- src/test/resources/persistence.properties
- src/test/samples/po.xml
- xmlのサンプルデータはPurchase Order Schema Example のpo.xmlを持ってきている
- pom.xml
(参考 : http://confluence.highsource.org/display/HJ3/Purchase+Order+Tutorial)
- チュートリアルでは、HSQLDBを使っているが、PostgreSQLに変更した。
- チュートリアルのサンプルのpom.xmlを見たらJUnitがコメントアウトされていた。
- 試しにコメントアウトを外して見たら、"TestCaseが解釈されない"みたいなエラーが発生
- JUnit 3で書かれているためか?
- 試しにコメントアウトを外して見たら、"TestCaseが解釈されない"みたいなエラーが発生
■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は以下のサイクルを実行している。
- XMLから作成したEntityとsrc/test/resources/persistence.propertiesを利用して, src/test/samplesにあるxmlファイルを全てunmarshallして、永続化する
- 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)
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()); } }
懸念事項
今のところ気になることが性能問題。 もう少し深堀してみる。
- JAXBContext, Schemaの生成にコストがかかる