Java 如何创建一个采用 XML 并将一些数据绑定到 JPA 注释的 POJO 的 Camel 路由?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9702158/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 06:25:16  来源:igfitidea点击:

How to create a Camel route which takes XML and bind some data to JPA annotated POJO?

javaxmljpajaxbapache-camel

提问by bbcooper

I am new to Apache Camel and mock testing so here it goes...

我是 Apache Camel 和模拟测试的新手,所以这里是......

I have an XML with no XSD schema which I have no influence on. Child elements of this XML hold data which I want to bind to my business pojo. This POJO (WeatherCurrent) is already JPA annotated and I was thinking of adding JAXB annotation so the splitted XML could be mapped to my POJO.

我有一个没有 XSD 模式的 XML,我对它没有影响。这个 XML 的子元素包含我想要绑定到我的业务 pojo 的数据。这个 POJO (WeatherCurrent) 已经被 JPA 注释了,我正在考虑添加 JAXB 注释,以便拆分的 XML 可以映射到我的 POJO。

As this XML has a root element and I only want its childs (metData) I have a problem how to annotate my POJO as I can not use @XmlRootElement.

由于此 XML 有一个根元素,而我只想要它的子元素 (metData),因此我无法使用 @XmlRootElement 来注释我的 POJO,因此我遇到了问题。

This is partialy described here: http://camel.apache.org/splitter.htmlat Streaming big XML payloads using Tokenizer languagechapter. My POJO is like orderxml element in that example. I need just a few elements out of metData xml element to map to my POJO fields.

此处部分描述了这一点:http: //camel.apache.org/splitter.html使用 Tokenizer 语言一章流式传输大型 XML 有效负载。我的 POJO 就像那个例子中的orderxml 元素。我只需要metData xml 元素中的几个元素来映射到我的POJO 字段。

There is also a chapter Partial marshalling/unmarshallingat http://camel.apache.org/jaxb.htmlbut there is no JAVA DSL example (a must), nor how to annotate the pojo to work with XML fragments.

还有一个章节部分编组/解组http://camel.apache.org/jaxb.html但没有JAVA DSL例子(必须的),也不怎么给POJO注释与XML片段的工作。

So far I have this test code:

到目前为止,我有这个测试代码:

import java.io.File;

import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.converter.jaxb.JaxbDataFormat;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;

public class WeatherCurrentTest extends CamelTestSupport {

    @EndpointInject(uri = "file:src/test/resources")
    private ProducerTemplate inbox;

    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                DataFormat jaxbDataFormat = new JaxbDataFormat("com.mycompany.model.entities.weather");// WARNING two packages for JaxbDataFormat

                from("file:src/test/resources/?fileName=observation_si_latest.xml&noop=true&idempotent=false")
                .split()
                .tokenizeXML("metData")
                .unmarshal(jaxbDataFormat)
                .to("mock:meteo");          
            }
        };
    }

    @Test
    public void testMetData() throws Exception {
        MockEndpoint mock = getMockEndpoint("mock:meteo");
        mock.expectedMessageCount(9);

        File meteo = new File("src/test/resources/observation_si_latest.xml");
        String content = context.getTypeConverter().convertTo(String.class, meteo);
        inbox.sendBodyAndHeader(content, Exchange.FILE_NAME, "src/test/resources/observation_si_latest.xml");

        mock.assertIsSatisfied();
    }

}

The XML (observation_si_latest.xml) comes in this form:

XML (observation_si_latest.xml) 采用以下形式:

<?xml version="1.0" encoding="UTF-8"?>
<data id="MeteoSI_WebMet_observation_xml">
    <language>sl</language>
    <metData>
        <domain_altitude>55</domain_altitude>
        <domain_title>NOVA GORICA</domain_title>
        <domain_shortTitle>BILJE</domain_shortTitle>
        <tsValid_issued>09.03.2012 15:00 CET</tsValid_issued>
        <t_degreesC>15</t_degreesC>
    </metData>
    <metData>
        <domain_meteosiId>KREDA-ICA_</domain_meteosiId>

I left out lots of elements of the metData elements for brevity. I want to map (amongs others) domain_title to my JPA annotated POJO's station field and later save it database, hopefully all in one smart and short Camel route.

为简洁起见,我省略了metData 元素的许多元素。我想将 domain_title (以及其他)映射到我的 JPA 注释的 POJO 的站字段,然后将其保存在数据库中,希望所有这些都在一条智能而短的 Camel 路线中。

The POJO (no JAXB annotations yet):

POJO(还没有 JAXB 注释):

@Entity
@Table(name="weather_current")
public class WeatherCurrent implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    private String station;

    @Temporal( TemporalType.TIMESTAMP)
    @Column(name="successfully_updated")
    private Date successfullyUpdated;

    private short temperature;

    @Column(name="wind_direction")
    private String windDirection;

}

I also left out lots of fields and methods.

我还遗漏了很多字段和方法。

So the idea is to map the value of the *domain_title* to WeatherCurrent POJO's stationfield and to do so for each metData element and save a list of WeatherCurrent objects to database.

所以我们的想法是将 *domain_title* 的值映射到 WeatherCurrent POJO 的字段,并为每个 metData 元素执行此操作,并将 WeatherCurrent 对象列表保存到数据库。

Any advice on the best way on how to implement this is welcome.

欢迎任何有关如何实现这一点的最佳方式的建议。

回答by bbcooper

It turns out I had one wrong assumption of not being able to use the @XmlRootElement. The route and the test passes with success after I annotated the POJO and added jaxb.index file beside it. Will post the solution later or tomorrow as I am on a train now.

事实证明,我有一个错误的假设,即无法使用 @XmlRootElement。在我注释 POJO 并在其旁边添加 jaxb.in​​dex 文件后,路由和测试成功通过。稍后或明天将发布解决方案,因为我现在在火车上。

Hours later...

几个小时以后...

The JAXB annotations on the POJO (on top of JPA ones):

POJO 上的 JAXB 注释(在 JPA 注释之上):

@Entity
@Table(name="weather_current")
@XmlRootElement(name = "metData")
@XmlAccessorType(XmlAccessType.FIELD)
public class WeatherCurrent implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    @XmlElement(name = "nn_shortText")
    private String conditions;

    @XmlElement(name = "rh")
    private short humidity;

    @XmlElement(name = "msl")
    private short pressure;

    @Column(name="pressure_tendency")
    @XmlElement(name = "pa_shortText")
    private String pressureTendency;

    @Temporal( TemporalType.TIMESTAMP)
    @XmlElement(name = "tsValid_issued")
    private Date published;

    @XmlElement(name = "domain_longTitle")
    private String station;

enabled me to get a list of WeatherCurrent Exchage objects. Just for test I routed each one to my EchoBean to print out one property:

使我能够获得 WeatherCurrent Exchage 对象的列表。只是为了测试,我将每个路由到我的 EchoBean 以打印出一个属性:

.unmarshal(jaxbDataFormat).bean(EchoBean.class, "printWeatherStation")

and EchoBean:

和 EchoBean:

public class EchoBean {

    public String printWeatherStation(WeatherCurrent weatherCurrent) {
        return weatherCurrent.getStation();
    }
}

nicely prints out the names of the weather stations with the log Camel component.

使用 log Camel 组件很好地打印出气象站的名称。

The one undocumented thing that bothered me was that I had to put this jaxb.index file next WeatherCurrent java source although at http://camel.apache.org/jaxb.htmlit clearly says that jaxb context is initialized with

困扰我的一个未记录的事情是我不得不把这个 jaxb.in​​dex 文件放在 WeatherCurrent java 源的下一个虽然在http://camel.apache.org/jaxb.html它清楚地表明 jaxb 上下文是用初始化的

DataFormat jaxb = new JaxbDataFormat("com.acme.model");