CRUD operations like a breeze for AdminFaces applications based on Apache DeltaSpike Data module.
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 |
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.
-
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. |
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;
} |
For an example project using Admin Persistence see admin-starter-persistence.
💡
|
For a composite-key demo see this branch of admin-starter.
|
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>