How to build a Camel route to JPA

In this tutorial we will learn how to persist some data on a PostgreSQL Database using a Camel route which is based on the Java Persistence API (JPA) defined in a project.

The steps to complete this tutorial are:

  1. Let’s start from a maven archetype
  2. Create the entity bean
  3. Configure the persistence.xml
  4. Configure the camel-context
  5. Define the camel route
  6. enjoy running it !

1. Camel Maven archetype

Start by creating the skeleton of your Camel project using its archetype:

mvn archetype:generate                   \   -DarchetypeGroupId=org.apache.camel.archetypes  \   -DarchetypeArtifactId=camel-archetype-spring   \   -DarchetypeVersion=3.0.0 \   -DgroupId=com.example.camel.jpa \   -DartifactId=camel-jpa \   -Dversion=1.0-SNAPSHOT

Next, we will adjust the dependencies in pom.xml so that we are able to use JPA, Hibernate and JAXB for marshalling:

<dependencyManagement>
  <dependencies>
    <!-- Camel BOM -->
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-parent</artifactId>
      <version>3.0.0</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-jaxb</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-jpa</artifactId>
  </dependency>
  <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.12</version>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-core</artifactId>
  </dependency>
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-spring</artifactId>
  </dependency>
  <!-- logging -->
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <scope>runtime</scope>
  </dependency>
  <!-- testing -->
  <dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-spring</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

Now let’s move to the code.

2. Create the Entity bean

The class Person represents the entity bean that will be inserted into the database:

package com.example.camel.jpa.model;

import javax.persistence.GenerationType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@XmlRootElement
@XmlType
@Entity(name = "Person")
public class Person {
  private Long id;
  private String name;
  private String surname;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getSurname() {
    return surname;
  }

  public void setSurname(String surname) {
    this.surname = surname;
  }

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }
}

To help jaxb to create the JAXBContext we add the file jaxb.index under the resources folder of the project, using the same package structure of your Model bean:

src/
├── data
│   └── Person.xml
└── main
    ├── java
    │   └── com
    │       └── example
    │           └── camel
    │               └── jpa
    │                   ├── model
    │                   │   └── Person.java
    │                   └── MyRouteBuilder.java
    └── resources
        ├── com
        │   └── example
        │       └── camel
        │           └── jpa
        │               └── model
        │                   └── jaxb.index

The jaxb.index file will contain just the name of the Class:

Person

3. Configure the persistence.xml

In the META-INF folder we add the following persistence.xml file with the jdbc properties to connect to the PostgreSQL database:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"              xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"              version="2.0"
  xmlns="http://java.sun.com/xml/ns/persistence">
  <persistence-unit name="persistenceUnit"                       transaction-type="RESOURCE_LOCAL">
    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
      <!-- DB Driver -->
      <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/cameldb" />
      <!-- BD Mane -->
      <property name="javax.persistence.jdbc.user" value="camel" />
      <!-- DB User -->
      <property name="javax.persistence.jdbc.password" value="camel" />
      <!-- DB Password -->
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect"/>
      <!-- DB Dialect -->
      <property name="hibernate.hbm2ddl.auto" value="create-drop" />
      <!-- create / create-drop / update -->
      <property name="hibernate.show_sql" value="true" />
      <!-- Show SQL in console -->
      <property name="hibernate.format_sql" value="true" />
      <!-- Show SQL formatted -->
    </properties>
  </persistence-unit>
</persistence>

4. Configure the camel-context

The following file, using spring, configures a camel context. A simple java route is added to the camel context.

In the camel context we add the jpa component. It needs also an EntityManagerFactory and a JpaTransactionManager.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Configures the Camel Context-->
<beans
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
  <camelContext trace="true"
    xmlns="http://camel.apache.org/schema/spring">
    <routeBuilder ref="myroute"/>
  </camelContext>
  <bean id="myroute" class="com.example.camel.jpa.MyRouteBuilder"/>
  <bean id="jpa" class="org.apache.camel.component.jpa.JpaComponent">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="transactionManager" ref="jpaTxManager"/>
  </bean>
  <bean id="jpaTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
  </bean>
  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="persistenceUnit"/>
  </bean>
</beans>

5. Define the camel route

The camel route is defined by MyRouteBuilder class in the configure method.

package com.example.camel.jpa;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.dataformat.JaxbDataFormat;

public class MyRouteBuilder extends RouteBuilder {
  public void configure() {
    JaxbDataFormat jxb = new JaxbDataFormat();
    jxb.setContextPath("com.example.camel.jpa.model");
    from("file:src/data?noop=true").unmarshal(jxb).to("jpa:com.example.camel.jpa.model.Person");
  }
}

A JaxbDataFormat transforms data from xml to a Person entity and the jpa component inserts it in the database. The XML is picked up from the data folder:

<?xml version="1.0" encoding="UTF-8"?><person>
        
   <name>Francesco</name>
        
   <surname>Marchioni</surname>
    
</person>

Here is the final view of the project:

camel jpa tutorial camel jpa tutorial

6. Enjoy running it

In order to test the project we will need a PostgreSQL database instance. For the sake of brevity we will just boot it with Docker as follows:

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name camel_jpa -e POSTGRES_USER=camel -e POSTGRES_PASSWORD=camel -e POSTGRES_DB=cameldb -p 5432:5432 postgres:10.5

Now you can build and run the project with:

mvn clean install camel:run

Check from the output that the Table Person has been created and one rows has been inserted (the trace=”true” option on the camel context make camel to trace the exchange transported in the route):

INFO  Created XMLInputFactory: com.sun.xml.internal.stream.XMLInputFactoryImpl@23759ec0. DOMSource/DOMResult may have issues with com.sun.xml.internal.stream.XMLInputFactoryImpl@23759ec0. We suggest using Woodstox. Hibernate:      select         nextval ('hibernate_sequence') Hibernate:      insert      into         Person         (name, surname, id)      values         (?, ?, ?) 

Perfect. You can also log into the Docker container image and check that the data is actually there:

$ docker ps CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES f19c77664c8d        postgres:10.5       "docker-entrypoint..."   About an hour ago   Up About an hour    0.0.0.0:5432->5432/tcp   camel_jpa  $ docker exec -it f19c77664c8d /bin/bash  # psql cameldb camel psql (10.5 (Debian 10.5-2.pgdg90+1)) Type "help" for help.  cameldb=# \dt;         List of relations  Schema |  Name  | Type  | Owner  --------+--------+-------+-------  public | person | table | camel (1 row)   cameldb=# select * from person;  id |   name    |  surname   ----+-----------+-----------   1 | Francesco | Marchioni (1 row) 

What about reading data from database?

Exercise: Try it:

public void configure() {
  JaxbDataFormat jxb = new JaxbDataFormat();
  jxb.setContextPath("com.example.camel.jpa.model");
  from("jpa:com.example.camel.jpa.model.Person?consumer.query=select o from com.example.camel.jpa.model.Person").marshal(jaxb).log("${body}");
}

Summary

We have just covered how to manage JPA marshalling using a Camel Route and the Camel JPA component.

The complete code is here:  https://github.com/fmarchioni/masterspringboot/tree/master/camel/camel-jpa