13 KiB
Spring Data JPA Repository Web Exporter
The Spring Data JPA Repository Web Exporter allows you to export your JPA Repositories as a RESTful web application. The exporter exposes the CRUD methods of a CrudRepository for doing basic entity management. Relationships can also be managed between linked entities. The exporter is deployed as a traditional Spring MVC Controller, which means all the traditional Spring MVC tools are available to work with the Web Exporter (like Spring Security, for instance).
Installation
Servlet environment
Deployment of the Spring Data Web Exporter is extremely flexible. You can build a WAR file for deploying in a Servlet 2.5 environment. You can drop the spring-data-rest-webmvc.jar artifact into an existing Servlet 3.0 application (the O'Reilly Spring Data book example application contains a Spring 3.1 WebApplicationInitializer). Start by cloning the base web application project: https://github.com/SpringSource/spring-data-rest-webmvc. This application contains a web.xml file. If you want the XML-free Servlet 3.0 version, simply delete src/main/webapp, create a WebApplicationInitializer as mentioned above, build the WAR file, and then deploy that to your Servlet 3.0 container (Jetty 8 or similar).
git clone https://github.com/SpringSource/spring-data-rest-webmvc.git
cd spring-data-rest-webmvc
./gradlew war
Deploy the built WAR file to your servlet container:
cp build/libs/spring-data-rest-webmvc-1.0.0.RC2.war $TOMCAT_HOME/webapps/data.war
cd $TOMCAT_HOME
bin/catalina.sh run
You can also deploy to a Jetty web container embedded in the build:
./gradlew jettyRun
The WAR file has a couple example domain classes and exposes a couple repositories by default. You can verify that this configuration is working by issuing an HTTP GET to the root of the web application:
curl -v http://localhost:8080/data/
In return, you should see:
> GET /data/ HTTP/1.1
> User-Agent: curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json;charset=ISO-8859-1
< Content-Length: 257
<
{
"_links" : [ {
"rel" : "address",
"href" : "http://localhost:8080/data/address"
}, {
"rel" : "person",
"href" : "http://localhost:8080/data/person"
}, {
"rel" : "profile",
"href" : "http://localhost:8080/data/profile"
} ]
}
Export Repositories
To expose your Repositories to the exporter, you can include a Spring XML configuration file in the classpath (e.g. in a client JAR or in WEB-INF/classes). The filename should end with "-export.xml" and reside under the path META-INF/spring-data-rest/. Your configuration should include a properly instantiated EntityManagerFactoryBean, an appropriate DataSource, and the appropriate repository configuration. It's easiest to use the special XML namespace for this purpose. An example configuration (named WEB-INF/spring-data-rest/repositories-export.xml) would look like something like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
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://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<import resource="shared.xml"/>
<jpa:repositories base-package="com.mycompany.domain.repositories"/>
</beans>
The file shared.xml contains a JDBC DataSource configuration, an EntityManagerFactoryBean, and a JpaTransactionManager.
You can also use JavaConfig to configure your application. Thanks to the new @EnableJpaRepositores annotation on @Configuration beans introduced in the latest Spring Data JPA release, you can bootstrap the Spring MVC controller by simply instantiating a RepositoryRestMvcConfiguration bean or by an @Import of the same.
Including your domain artifacts
To expose your domain objects (your JPA entities, Repositories) and Spring configuration using the web exporter, you need to copy those resources to the web exporter's WEB-INF/lib or WEB-INF/classes directory. There are potentially other ways to deploy these artifacts without modifying the web exporter's WAR file, but those methods are considerably more complicated and prone to classpath problems. The easiest and most reliable way to deploy your user artifacts are by deploying them alongside the web exporter's artifacts.
Exposing your repositories
By default, any repositories found are exported using the bean name of the repository in the Spring configuration (minus the word "Repository", if it appears in the bean name).
If you have a JPA entity in your domain model that looks like...
@Entity
public class Person {
@Id
private Long id;
private String name;
@Version
private Long version;
@OneToMany
private List<Address> addresses;
@OneToMany
private Map<String, Profile> profiles;
}
...and an appropriate CrudRepository interface defined like...
public interface PersonRepository extends CrudRepository<Person, Long> {
}
...your PersonRepository will by default be declared in the ApplicationContext with a bean name of "personRepository". The web exporter will strip the word "Repository" from it and expose a resource named "person". The resulting URL of this repository (assuming the exporter webapp is deployed at context path /data in your servlet container) will be http://localhost:8080/data/person.
You can configure under what path, or whether a resource is exported at all, by using the @RestResource annotation. Details are here: Configuring the REST URL path
Discoverability
The Web Exporter implements some aspects of the HATEOAS methodology. That means all the services of the web exporter are discoverable and exposed to the client using links.
If you issue an HTTP request to the root of the exporter:
curl -v http://localhost:8080/data/
You'll get back a chunk of JSON that points your user agent to the locations of the exported repositories:
{
"links" : [{
"rel" : "person",
"href" : "http://localhost:8080/data/person"
}]
}
The "rel" of the link will match the exported name of the repository. Your application should keep track of this rel value as the key to this repository.
Similarly, if you issue a GET to http://localhost:8080/data/person, you should get back a list of entities exported at this resource (as returned by the CrudRepository.findAll method). See the wiki for more information about the paging and sorting options.
curl -v http://localhost:8080/data/person
{
"content": [ ],
"links" : [ {
"rel" : "person.search",
"href" : "http://localhost:8080/data/person/search"
} ]
}
The default "rel" of these links will be the rel of the repository plus a dot '.' plus the simple class name of the entity managed by this repository. The rel value can be configured using the @RestResource annotation, discussed on Configuring the REST URL path.
Following these links will give your user agent a chunk of JSON that represents the entity. Besides properly handling nested objects and simple values, the web exporter will show relationships between entities using links just like those presented previously.
curl -v http://localhost:8080/data/person/1
{
"name" : "John Doe",
"links" : [ {
"rel" : "profiles",
"href" : "http://localhost:8080/data/person/1/profiles"
}, {
"rel" : "addresses",
"href" : "http://localhost:8080/data/person/1/addresses"
}, {
"rel" : "self",
"href" : "http://localhost:8080/data/person/1"
} ],
"version" : 1
}
This entity has a simple String value called "name", and two relationships to other entities ("profiles", and "addresses"). Note that the "rel" value of the link corresponds to the property name of the @Entity.
The "self" link will always point to the resource for this entity. Use the "self" link to access the entity itself if you wish to update or delete the entity.
Following the links for the "profiles" property gives us a list of links to the actual entities that are referenced by this relationship:
curl -v http://localhost:8080/data/person/1/profiles
{
"profiles" : [ {
"rel" : "twitter",
"href" : "http://localhost:8080/data/person/1/profiles/1"
}, {
"rel" : "facebook",
"href" : "http://localhost:8080/data/person/1/profiles/2"
} ]
}
In this case, the "profiles" property is a Map, so the "rel" value of the links is the key in the Map. The resource link, however, does not use the Map key in the URL. It is consistent with all other links to child resources and uses the ID of the child entity as the last component of the URL.
Retrieving the linked entity gives us a JSON representation of the entity, as well as the "self" link necessary to update and delete the entity.
curl -v http://localhost:8080/data/person/1/profiles/1
{
"links" : [ {
"rel" : "self",
"href" : "http://localhost:8080/data/profile/1"
} ],
"type" : "twitter",
"url" : "#!/johndoe"
}
Updating relationships
To maintain a relationship between two entities, access the resource of the relationship by using the id of the entity as the last element in the resource path. For example, to add a link to a Profile with id 3 to a Person with id 1, issue a POST to the "profiles" resource and include in the body of the request a list of resource paths to entities you want to link to (make sure to use the special Content-Type "text/uri-list" which, as the name implies, is a representation of a list of URIs):
curl -v -X POST -H "Content-Type: text/uri-list" -d "http://localhost:8080/data/profile/3" http://localhost:8080/data/person/1/profiles
You can also delete a relationship by issuing a DELETE request to the resource path that represents the relationship between parent and child entities. For example, to delete a relationship between a Profile entity with an id of 2 and a Person with an id of 1:
curl -v -X DELETE http://localhost:8080/data/person/1/profiles/2
Calling Query methods
Starting with Spring Data REST 1.0.0.M2, the exporter exposes Repository query methods under the special URL path /repository/search/*.
To see what query methods are exported, issue a GET request to the entity resource URL and add the segment "search". You'll get back a list of links to the exported search methods.
curl -v http://localhost:8080/data/person/search
{
"links" : [ {
"rel" : "person.findByName",
"href" : "http://localhost:8080/data/person/search/findByName"
} ]
}
To query for entities using this search method, add a query parameter to the URL. The response will be a list of links to the top-level URL for that resource.
curl -v http://localhost:8080/data/person/search/findByName?name=John+Doe
[ {
"rel" : "person.Person",
"href" : "http://localhost:8080/data/person/1"
} ]
To change the URL under which the query method is exported or set the name of the query parameter containing the search term, use the @RestResource annotation.
@RestResource(path = "people")
public interface PersonRepository extends CrudRepository<Person, Long> {
@RestResource(path = "name", rel = "names")
public List<Person> findByName(@Param("name") String name);
}
This changes the path the PersonRepository is exported under to /people, changes the rel of the search URL to people.names, changes the path under with the query method is exported to /name, and sets the query parameter containing the search term to name. To search the Repository using this method, issue a GET request.
curl -v http://localhost:8080/data/people/search/name?name=John+Doe