-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add "Database" section #72
Comments
I use only JPA/Hibernate + Guice Persist. If you want I can write about it later. |
The steps below describe how to use Pippo with JPA/Hibernate + Guice PersistIn this example
Let's now configure JPA/Hibernate + Guice Persist for use in our application. Initially we need to define some dependencies in <properties>
<hibernate.version>5.2.1.Final</hibernate.version>
<google.guice.version>4.0</google.guice.version>
</properties>
<dependencies>
<!-- Engine to Pippo convert to Json -->
<dependency>
<groupId>ro.pippo</groupId>
<artifactId>pippo-gson</artifactId>
<version>${pippo.version}</version>
</dependency>
<!-- In this example we will work with Controllers -->
<dependency>
<groupId>ro.pippo</groupId>
<artifactId>pippo-controller</artifactId>
<version>${pippo.version}</version>
</dependency>
<!-- Our controllers will be able to inject Guice components -->
<dependency>
<groupId>ro.pippo</groupId>
<artifactId>pippo-guice</artifactId>
<version>${pippo.version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- Guice extension to manager the EntityManager lifecycle -->
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-persist</artifactId>
<version>${google.guice.version}</version>
</dependency>
<!-- H2 database driver -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.194</version>
</dependency>
</dependencies> Now we need to set up a JPA peristence unit in /src/main/java/META-INF/ <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="pippojpaPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<properties>
<!-- Oracle -->
<!--
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
-->
<!-- Postgres -->
<!--
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect" />
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
-->
<!-- H2 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<!-- Hibernate 5.2: none (default value), create-only, drop, create, create-drop, validate, and update -->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!--
<property name="hibernate.hbm2ddl.import_files" value="importInicial.sql,import.sql" />
-->
<property name="javax.persistence.jdbc.url" value="jdbc:h2:./db/h2/pippojpa/pippojpa;AUTO_SERVER=true" />
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value="sa"/>
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.use_sql_comments" value="true" />
<property name="hibernate.format_sql" value="false" />
<!-- AUTO : The Session is sometimes flushed before query execution -->
<!-- COMMIT: The Session is only flushed prior to a transaction commit -->
<property name="org.hibernate.flushMode" value="COMMIT"/> <!-- default: AUTO -->
<property name="hibernate.connection.autocommit" value="false"/> <!-- default is false -->
<property name="hibernate.connection.pool_size" value="3"/>
<property name="hibernate.enable_lazy_load_no_trans" value="false"/> <!-- default is false -->
</properties>
</persistence-unit>
</persistence> We will need a filter to enable the HTTP Request unit of work and to have guice-persist manage the lifecycle of active units of work. In other words: to manage the EntityManager during the HTTP request lifecycle. import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Injector;
import com.google.inject.persist.UnitOfWork;
import ro.pippo.core.route.RouteContext;
import ro.pippo.core.route.RouteHandler;
/**
* Filter created based on {@link com.google.inject.persist.PersistFilter}.
* <p>
* https://github.com/google/guice/wiki/JPA
* </p>
*/
class PersistFilter implements RouteHandler {
private static final Logger log = LoggerFactory.getLogger(PersistFilter.class);
private static final String KEY_UNIT_OF_WORK = "UnitOfWork";
private final boolean begin;
private final Injector injector;
private PersistFilter() {
throw new RuntimeException("instancing is not allowed");
}
private PersistFilter(Injector injector, boolean begin) {
this.begin = begin;
this.injector = injector;
}
static PersistFilter buildBegin(Injector injector) {
return new PersistFilter(injector, true);
}
static PersistFilter buildEnd() {
return new PersistFilter(null, false);
}
@Override
public void handle(RouteContext routeContext) {
log.trace("PersistFilter, begin: {} (end: {})", begin, !begin);
if (begin) { // begin
final UnitOfWork unitOfWork = begin();
routeContext.setLocal(KEY_UNIT_OF_WORK, unitOfWork);
routeContext.next();
} else { // end
final UnitOfWork unitOfWork = routeContext.removeLocal(KEY_UNIT_OF_WORK);
if (unitOfWork != null) {
end(unitOfWork);
}
}
}
private UnitOfWork begin() {
log.trace("unitOfWork.begin");
UnitOfWork unitOfWork = injector.getInstance(UnitOfWork.class);
unitOfWork.begin();
return unitOfWork;
}
private void end(UnitOfWork unitOfWork) {
log.trace("unitOfWork.end");
if (unitOfWork != null) {
unitOfWork.end();
}
}
} For Guice to manage the EntityManager it needs to know the persistence unit to create the instances in the dependency injections. import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.persist.jpa.JpaPersistModule;
public final class JPAGuiceModule implements Module {
@Override
public void configure(Binder binder) {
binder.install(new JpaPersistModule("pippojpaPersistenceUnit"));
}
} Let's now build a JPA entity, a DAO, and a controller to test our configuration. Below is a simple JPA entity called import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class City implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, updatable = false, nullable = false)
private Long id;
@Column(name = "NAME", nullable = false)
private String name;
public City() {
super();
}
public City(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "City [id=" + id + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
City other = (City) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
}
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.persist.Transactional;
public class CityDAO {
@Inject
private Provider<EntityManager> emProvider;
private EntityManager getEntityManager() {
return emProvider.get();
}
// Transaction is not required
public List<City> getAll() {
final String qlString = "FROM " + City.class.getName();
TypedQuery<City> query = getEntityManager().createQuery(qlString, City.class);
return query.getResultList();
}
// Transaction is not required
public City getById(Serializable id) {
return getEntityManager().find(City.class, id);
}
@Transactional
public City save(City city) {
City entity = persist(city);
getEntityManager().flush();
return entity;
}
@Transactional
public void deleteById(Serializable id) {
City entity = getEntityManager().find(City.class, id);
// Case of attached entity - simply remove it
if (getEntityManager().contains(entity)) {
getEntityManager().remove(entity);
} else {
// Case of unattached entity, first it is necessary to perform
// a merge, before doing the remove
entity = getEntityManager().merge(entity);
getEntityManager().remove(entity);
}
}
private <T> T persist(T entity) {
// Case of new, non-persisted entity
if (extractId(entity) == null) {
getEntityManager().persist(entity);
} else if (!getEntityManager().contains(entity)) {
// In the case of an attached entity, we do nothing (it
// will be persisted automatically on synchronisation)
// But... in the case of an unattached, but persisted entity
// we perform a merge to re-attach and persist it
entity = getEntityManager().merge(entity);
}
return entity;
}
private Object extractId(Object entity) {
return getEntityManager().getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(entity);
}
}
import java.util.List;
import com.google.inject.Inject;
import ro.pippo.controller.Controller;
import ro.pippo.controller.DELETE;
import ro.pippo.controller.GET;
import ro.pippo.controller.POST;
import ro.pippo.controller.Path;
import ro.pippo.controller.Produces;
import ro.pippo.controller.extractor.Bean;
import ro.pippo.controller.extractor.Param;
@Path("/pages")
public class CityController extends Controller {
@Inject
private CityDAO cityDAO;
@GET("/city/getAll")
@Produces(Produces.JSON)
public List<City> cities() {
return cityDAO.getAll();
}
@GET("/city/get/{id}")
@Produces(Produces.JSON)
public City getCity(@Param("id") Long id) {
return cityDAO.getById(id);
}
@DELETE("/city/delete/{id}")
@Produces(Produces.TEXT)
public String delete(@Param("id") Long id) {
cityDAO.deleteById(id);
return "City with id " + id + " was deleted";
}
@POST("/city/save")
@Produces(Produces.JSON)
public City save(@Bean City city) {
return cityDAO.save(city);
}
} Finally we need to make final configurations in the PippoApplication.onInit() import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.persist.PersistService;
import ro.pippo.controller.ControllerApplication;
import ro.pippo.guice.GuiceControllerFactory;
public class PippoApplication extends ControllerApplication {
@Override
protected void onInit() {
// http://docs.jboss.org/hibernate/orm/5.2/topical/html_single/logging/Logging.html
System.setProperty("org.jboss.logging.provider", "slf4j"); // Required for Hibernate when using slf4j
Module guiceModule = new JPAGuiceModule();
Injector injector = Guice.createInjector(guiceModule); // create guice injector
setControllerFactory(new GuiceControllerFactory(injector)); // register GuiceControllerFactory
PersistService service = injector.getInstance(PersistService.class);
service.start(); // starts the persistence unit
// This filter should be applied only to contexts that may require the EntityManager
ANY("/pages/.*", PersistFilter.buildBegin(injector));
addControllers(CityController.class);
// Here your other controllers, routes, configurations, ...
// Cleanup - It should actually be the last call of the onInit() method
ANY("/pages/.*", PersistFilter.buildEnd()).runAsFinally();
}
} Here we have Pippo fully configured to work with Hibernate / JPA, including with the automatic management of resources allocated by EntityManager through the HTTP filter created. Launch the application... Lets test... Let's use cURL to make requests. $ # getting all cities, must be empty
$ curl http://localhost:8338/pages/city/getAll
[]
$ # inserting three cities and getting the IDs
$ curl -X POST http://localhost:8338/pages/city/save -H 'content-type: multipart/form-data' -F name=city1
{"id":1,"name":"city1"}
$ curl -X POST http://localhost:8338/pages/city/save -H 'content-type: multipart/form-data' -F name=city2
{"id":2,"name":"city2"}
$ curl -X POST http://localhost:8338/pages/city/save -H 'content-type: multipart/form-data' -F name=city3
{"id":3,"name":"city3"}
$ # getting all cities (there must be three)
$ curl http://localhost:8338/pages/city/getAll
[{"id":1,"name":"city1"},{"id":2,"name":"city2"},{"id":3,"name":"city3"}]
$ # query the city with ID 1
$ curl http://localhost:8338/pages/city/get/1
{"id":1,"name":"city1"}
$ # delete the city with ID 2
$ curl -X DELETE http://localhost:8338/pages/city/delete/2
City with id 2 was deleted
$ # getting all cities, but the city with ID 2 no longer available
$ curl http://localhost:8338/pages/city/getAll
[{"id":1,"name":"city1"},{"id":3,"name":"city3"}]
$ # done! |
⬆️ |
@mhagnumdw It's OK. Thanks! |
Explain how to deal with database.
It's simple and natural to work with micro/lite JDBC frameworks like:
Show some examples code from real life application.
The text was updated successfully, but these errors were encountered: