Creating Java JPA entities with an XML field

Creating Java JPA entities with an XML field

1. Introduction

In this article we explore how you can store an XML field in your JPA entities.

We use a number of technologies:

  • Spring Data
  • Hibernate ORM
  • JAXB
  • PostgreSQL

As always, all code used within the article is available on GitHub: here.

If you would like to see how you can store a JSON field in a JPA entity check out the tutorial: here.

2. Our Goal

Our goal is to be able to perform create, read, update and delete (CRUD) operations on a JPA entity that contains a field which uses PostgreSQL’s native XML column type. To illustrate how to achieve this we will create:

  • a simple domain model with an XML field in an entity
  • a spring data JPA repository for our entity
  • a Spring MVC controller which exposes endpoints illustrating CRUD operations

Let’s get started!

3. Our domain model

For our domain model we have a Person and an Address:

  • Person
    • A person contains an ID, first name, surname and an Address.
  • Address
    • An address contains a door number, road name, and postal code.
    • The address is stored in a column in the database which has been defined to use the native
      xml column type.

3.1 The Address class

We define the Address class as an XML complex type:

3.2 The Person JPA entity

We define a person as a JPA entity. The entity definition is somewhat complex, so let’s go through it piece by piece. First, lets take a look at the field definitions:

We have an ID which is auto-generated by the database, and we also have the person’s first_name, surname and address:

  • The id, firstName, and surname are simple String fields.
  • The Address is where it gets interesting:
    • We have a transient field which is of the Address type we saw earlier (with XML annotations).
      • Transient here means it is not persisted to the database.
    • We also have another field called addressAsXmlString which is of type String:
      • This field represents the XML string version of the Address POJO.
    • Since we are using PostgreSQL’s native XML type as the type of the column we add
      columnDefinition = "xml".
    • We also use the @org.hibernate.annotations.Type annotation and provide a fully qualified
      class name as the type.

      • You can see the full source code for the SQLXMLType class here.
      • We won’t get into the details about the SQLXMLType class’ code in this article but you can think of it as providing the necessary functionality for Hibernate to work with the XML field.

3.2.1 XML Infrastructure Prerequisites

The addressAsXmlString field in the Person entity does not have a setter method and is private so its state cannot be mutated outside of this class (unless you are using reflection trickery):

  • It is our responsibility to make sure that it always accurately represents the contents of the Address POJO as an XML String.
  • With that in mind we need to define the setter for the Address POJO so that it also sets the addressAsXmlString value.
  • But before doing that we need to have the XML infrastructure in place to be able to marshal/unmarshall between an XML string/POJO.
    • For this we define a class, ModelJaxBContext which is the XML JaxB context for our XML model classes (which in this case is just the Address).
    • The full definition of the class is available here but for the purpose of this article will suffice to simply show the public interface:

3.2.2 Person entity continued…

Now we define our setter in the Person entity class for the Address as the following:

This way when we set the address POJO value, we are always updating the string representation as well.

We now have one final problem to solve – since the Address POJO is defined as transient (transient private Address address;) when the entity is loaded, then the address field will be set to null:

  • In order to avoid this we add a @PostLoad annotated method which sets the value of the Address when the entity is loaded:

4. CRUD operations

We now want to illustrate how we can perform CRUD operations on the entity and have the address persisted as an XML field in the PostgreSQL database. We do this in two steps:

  • Creating a Spring Data JPA repository for the entity.
  • Creating a @RestController exposing CRUD operations on the entity as HTTP endpoints.

4.1 The @Repository

Spring Data JPA repositories allow you to define a type signature and have a repository proxy which implements common CRUD operations on your entity injectable in other your components.

Let’s define our @Repository:

Here we are stating our repository operates on the Person entity and ID field is of type Long.

4.2 The @RestController

4.2.1 Ensure the repository is injected…

Now let’s create a @RestController which exposes CRUD functionality on our entity through HTTP Rest endpoints. We’ll slowly build up the code for the class as we work through more functionality (the full code can be found here):

Here we are using constructor injection to ensure that the controller will have an instance of the repository we defined in the previous subsection.

4.2.2 Create our PersonDTO

We want the CRUD functionality to return a Person representation so we define a PersonDTO class which we will return as a response to requests:

There are a number of reasons why we use a data transfer object (DTO) rather than the Person @Entity directly:

  • It allows flexibility in that the representation can differ from the actual entity if need be (in this case it does not).
  • We know definitively what object will be serialised in the HTTP response.
    • If we are responding with the Person @Entity rather than the PersonDTO the response serialiser may fail trying to serialise an object which is actually a Hibernate proxy (or something else) under the hood.
      • By using a DTO – what you see is what you get.

4.2.3 Create our rest endpoints

With the PersonDTO defined let’s create our REST endpoints:

Our rest endpoints are somewhat self-explanatory:

  • the @GetMapping retrieves an existing entity
  • the @PostMapping creates a new entity
  • the @PutMapping updates an existing entity
  • the @DeleteMapping deletes the entity with the given ID.

5. Testing the @RestController

If you run the associated code (available on github here) as a spring boot application (run the :bootRun Gradle task) you can navigate to http://localhost:8080/swagger-ui.html and have access to a Swagger UI GUI to make mock requests to the controller and see how everything works out:

Rest endpoint UI

The Swagger UI you can use to easily manually test the controller

For the purposes of this article I will use CURL rather than the UI to test the controller endpoints in the following sections.

5.1 Creating an entity

We send a POST request with the following request body (-d = data flag):

And receive a response:

From the response we can see that the newly created Person has been given an ID of 6. Let’s inspect the PostgreSQL database using PgAdmin to see how this is being stored there:
Illustrates the XML field has been stored in the DB.

We can see from the screenshot of the query result that the address has successfully been stored as XML within the database.

5.2 Updating an entity

Let’s update our newly created entry’s address:

If we take a look at the database again we can see the XML for the row with ID 6 has been updated:

Updated XML field in the database

6. Conclusion

In this article we explored how we can store an XML field in a JPA entity. We covered a lot of ground including creating the necessary JAXB classes to allow for you to marshal/unmarshall between XML strings and POJOs. Finally, we created a @RestController which exposed CRUD endpoints for the Person resource and we saw how the data is persisted as XML in the underlying database.

Share

Leave a Reply

Your email address will not be published.