Admin Persistence
CRUD operations like a breeze for AdminFaces applications based on Apache DeltaSpike Data module.
1. Prerequisites
This module depends on JSF
, CDI
and JPA
and was tested with respective implementations and versions:
JSF |
CDI |
JPA |
Mojarra 2.2 |
Weld 2.3 |
Hibernate 5.0 |
Mojarra 2.2 |
Weld 2.2 |
Hibernate 4.3 |
MyFaces 2.1 |
OpenWebBeans 1.7.4 |
Eclipselink 2.6 |
2. Usage
Following are the steps you need to follow in order to use Admin Persistence
:
-
Classpath
First include it in your classpath:
<dependency> <groupId>com.github.adminfaces</groupId> <artifactId>admin-persistence</artifactId> <version>1.0.7</version> </dependency>
❗ Admin persistence will bring the following transitive dependencies:
<dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>7.0</version> </dependency> <dependency> <groupId>org.apache.deltaspike.core</groupId> <artifactId>deltaspike-core-impl</artifactId> <version>1.8.2</version> </dependency> <dependency> <groupId>org.apache.deltaspike.core</groupId> <artifactId>deltaspike-core-api</artifactId> <version>1.8.2</version> </dependency> <dependency> <groupId>org.apache.deltaspike.modules</groupId> <artifactId>deltaspike-data-module-api</artifactId> <version>1.8.2</version> </dependency> <dependency> <groupId>org.apache.deltaspike.modules</groupId> <artifactId>deltaspike-data-module-impl</artifactId> <version>1.8.2</version> </dependency>
Of cource you can override them in your pom.xml as needed.
-
JPA Metamodel
As Admin Persistence uses DeltaSpike
typesafe
criteria you’ll need to generate JPA metamodel. There are various ways to do that, here is a maven plugin example:<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <id>metamodel</id> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/metamodel</outputDirectory> <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>4.3.8.Final</version> </dependency> </dependencies> </plugin>
💡 See this tutorial for configuring it on your IDE. -
Entity Manager
Admin persistence needs an exposed
entity manager
as CDI Bean, you can do that by using a CDI producer:@ApplicationScoped public class EntityManagerProducer { @PersistenceContext EntityManager em; @Produces public EntityManager produce() { return em; } }
-
Persistence Entity
Every JPA entity must be typed as a PersistenceEntity, it is an interface with only a method,
getId()
:import com.github.adminfaces.persistence.model.PersistenceEntity; @Entity @Table(name = "car") public class Car implements PersistenceEntity { @Override public Integer getId() { return id; } }
💡 You can extend
BaseEntity to gainequals()
,hashCode()
andtoString()
. -
Service layer
Now to create a service which will hold your business logic you need to extend CrudService:
@Stateless public class CarService extends CrudService<Car, Integer> { }
💡 Full source code for CarService can be found here. ℹ️ For some examples of CrudService usage see integration tests here. -
Controller
Finally on the controller layer (JSF managed beans) you need to extend CrudMB which will enable CRUD support for your JSF pages:
@Named @ViewScoped public class CarListMB extends CrudMB<Car> implements Serializable { @Inject CarService carService; @Inject @Service CrudService<Car, Integer> crudService; //generic injection @Inject public void initService() { setCrudService(carService); (1) } }
-
Needed by CrudMB otherwise it will throw an exception asking for CrudService initialization.
💡 You can use @BeanService annotation to provide CrudService:
@Named @ViewScoped @BeanService(CarService.class)//use annotation instead of setter injection public class CarListMB extends CrudMB<Car> implements Serializable { }
💡 Full source code for CarListMB can be found here. -
3. Pagination
Real pagination involves lots of boilerplate code, in admin-persistence it is a matter of using a Primefaces lazy datatable and bind it to the CrudMB list
variable:
<p:dataTable widgetVar="carsTable" var="c" value="#{carListMB.list}"
rows="5" rowKey="#{c.id}"
lazy="true" paginator="true"
<!-- other attributes -->
|
Full source code for this xhtml page can be found here. |
4. Pagination filtering
For filtering on the lazy datatable you just need to override configRestrictions
method in the managed bean’s service (the service we set in CarListMB) and add your restrictions based on a filter:
protected Criteria<Car, Car> configRestrictions(Filter<Car> filter) {
Criteria<Car, Car> criteria = criteria();
//create restrictions based on parameters map
if (filter.hasParam("id")) {
criteria.eq(Car_.id, filter.getIntParam("id"));
}
if (filter.hasParam("minPrice") && filter.hasParam("maxPrice")) {
criteria.between(Car_.price, filter.getDoubleParam("minPrice"), filter.getDoubleParam("maxPrice"));
} else if (filter.hasParam("minPrice")) {
criteria.gtOrEq(Car_.price, filter.getDoubleParam("minPrice"));
} else if (filter.hasParam("maxPrice")) {
criteria.ltOrEq(Car_.price, filter.getDoubleParam("maxPrice"));
}
//create restrictions based on filter entity
if (has(filter.getEntity())) {
Car filterEntity = filter.getEntity();
if (has(filterEntity.getModel())) {
criteria.likeIgnoreCase(Car_.model, "%"+filterEntity.getModel());
}
if (has(filterEntity.getPrice())) {
criteria.eq(Car_.price, filterEntity.getPrice());
}
if (has(filterEntity.getName())) {
criteria.likeIgnoreCase(Car_.name, "%"+filterEntity.getName());
}
}
return criteria;
}
|
<div class="ui-g-12">
<p:outputLabel for="model" value="#{msg['label.model']}"/>
</div>
<div class="ui-g-12">
<p:selectOneMenu id="model" value="#{carListMB.filter.entity.model}">
<f:selectItem itemLabel="Chose a model" itemValue=""/>
<f:selectItems value="#{models}" var="m" itemLabel="#{m}"
itemValue="#{m}"/>
</p:selectOneMenu>
</div>
<div class="ui-g-12">
<p:outputLabel for="name" value="#{msg['label.name']}"/>
</div>
<div class="ui-g-12">
<p:inputText id="name" value="#{carListMB.filter.entity.name}"/>
</div>
<div class="ui-g-6 ui-sm-12 ui-g-nopad">
<div class="ui-g-12">
<p:outputLabel for="min" value="#{msg['label.minPrice']}"/>
</div>
<div class="ui-g-12">
<p:inputNumber id="min" value="#{carListMB.filter.params.minPrice}"/>
</div>
</div>
<div class="ui-g-6 ui-sm-12 ui-g-nopad">
<div class="ui-g-12">
<p:outputLabel for="max" value="#{msg['label.maxPrice']}"/>
</div>
<div class="ui-g-12">
<p:inputNumber id="max" value="#{carListMB.filter.params.maxPrice}"/>
</div>
</div>
|
|
Any datatable update (ajax or not) will trigger the configRestrictions. |
|
Besides filtering the filter helper class also holds pagination and sort information. |
|
By default filters are saved on You can change this behavior by overriding keepFiltersInSession method on your Bean:
CarListMB
@Override
public boolean keepFiltersInSession() {
return false;
}
|
5. Sample application
For an example project using Admin Persistence see admin-starter-persistence.
|
For a composite-key demo see this branch of admin-starter. |
6. Snapshots
Snapshots are published to maven central on each commit, to use it just declare the repository below on your pom.xml
:
<repositories>
<repository>
<snapshots/>
<id>snapshots</id>
<name>libs-snapshot</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>