I’m starting the ‘quickstart series’ – the initial post will be about these four technologies, integrated and fully functional.
The project is built with Maven and Eclipse, sou you may be able to checkout it and import it on your Eclipse (or other environments compatible with Eclipse, like Netbeans).
It is configured with:
Spring and Spring Security 3.0.5
Jersey 1.9
Hibernate 3.4.0
JPA 1.0
MySQL 5.1
If you want a different configuration, feel free to modify it as you wish and, if you don’t mind, please take a few minutes to update the repository. By the way, it is https://github.com/alesaudate/kickstart-springjerseyhibernate. Feel free to clone it and have a bootstrap on your project.
Enjoy!
When: When you need Spring to manage Jersey´s libraries, but still want to be free to develop your services using JAX-RS.
When not: If your team does not know REST, maybe it´s not a good idea to use it in development, because REST has its own culture – Uniform Interfaces, Hypermedia, the concept of Resource-Oriented, etc.
How:
BaseEntity.java
package com.alesaudate.domain;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;
import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Version
private Long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
/**
Validation code, that is valid for all subclasses.
*/
public void validate () throws ValidationException {
ClassValidator validator = new ClassValidator(getClass());
InvalidValue[] invalidValues = validator.getInvalidValues(this);
if (invalidValues != null && invalidValues.length > 0) {
throw new ValidationException(buildValidationExceptionMessage(invalidValues));
}
}
public String buildValidationExceptionMessage (InvalidValue[] invalidValues) {
StringBuilder builder = new StringBuilder();
for (InvalidValue value : invalidValues) {
builder.append(value.toString()).append("\n");
}
return builder.toString();
}
}
BaseService.java
package com.alesaudate.services;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.InvalidStateException;
import com.alesaudate.domain.Person;
import com.alesaudate.services.support.collections.Collection;
@Component
public abstract class BaseService<T extends BaseEntity> {
@Autowired
private HibernateTemplate hibernateTemplate;
@Transactional
public T createOnDatabase (T entity) throws InvalidStateException {
entity.validate();
getHibernateTemplate().persist(entity);
return entity;
}
@POST
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T create (T entity) throws InvalidStateException {
return createOnDatabase(entity);
}
@Transactional
public T updateOnDatabase (T entity) throws InvalidStateException {
entity.validate();
getHibernateTemplate().update(entity);
return entity;
}
@PUT
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T update (T entity) throws InvalidStateException {
return updateOnDatabase(entity);
}
@Transactional(readOnly=true)
public T findOnDatabase (Long id) {
DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass()).add(Restrictions.eq("id", id));
List entities = getHibernateTemplate().findByCriteria(criteria);
if (entities.isEmpty())
return null;
return (T)entities.get(0);
}
@GET
@Path("{id}")
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public T find (@PathParam("id")Long id) {
T entity = findOnDatabase(id);
loadEntity(entity);
return entity;
}
@Transactional(readOnly=true)
public List<T > findAllFromDatabase () {
DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass());
List<T> all = getHibernateTemplate().findByCriteria(criteria);
loadList(all);
return all;
}
@GET
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Collection
public List<T> findAll() {
return findAllFromDatabase();
}
@Transactional
public void deleteFromDatabase (T toDelete) {
getHibernateTemplate().delete(toDelete);
}
@DELETE
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public void delete (T toDelete) {
deleteFromDatabase(toDelete);
}
public abstract Class<? extends BaseEntity> getManagedClass();
public abstract void loadEntity (T data);
public abstract void loadList (java.util.Collection<T> data);
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
}
PersonService.java
package com.alesaudate.services;
import java.util.Collection;
import javax.ws.rs.Path;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.alesaudate.domain.Address;
import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.Person;
@Component
@Path("/person")
public class PersonService extends BaseService<Person>{
@Override
public Class<? extends BaseEntity> getManagedClass() {
return Person.class;
}
@Override
public void loadEntity(Person data) {
//Load addresses
data.getAddresses();
}
@Override
@Transactional(propagation=Propagation.MANDATORY)
public void loadList(Collection<Person> data) {
for (Person p : data) {
getHibernateTemplate().find("select p.addresses from Person p");
for (Address address : p.getAddresses()) {
getHibernateTemplate().evict(address);
address.makeXMLCompatible();
}
getHibernateTemplate().evict(p);
}
}
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.alesaudate.services" />
<!-- Other bean definitions... -->
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Architecture</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>Jersey Servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
End.