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 typesafecriteria 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 manageras 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 extendBaseEntity 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 filterhelper 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-keydemo 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> 
     JarCasting
 JarCasting