Skip to content
Provides CRUD utilities for AdminFaces based applications.
Branch: master
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github
src fixes #40 Feb 24, 2019
.gitignore Fixes #30 Jan 15, 2019
.travis.yml Adds it tests and coverage report Sep 1, 2017
LICENSE.txt initial drop in Aug 24, 2017
README.adoc
metamodel.png README update Aug 24, 2017
pom.xml
settings.xml

README.adoc

Admin Persistence

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:

  1. Classpath

    First include it in your classpath:

    <dependency>
        <groupId>com.github.adminfaces</groupId>
        <artifactId>admin-persistence</artifactId>
        <version>1.0.5</version>
    </dependency>
    ❗️

    Admin persistence will bring the following transitive dependencies:

    <dependency>
        <groupId>org.primefaces</groupId>
        <artifactId>primefaces</artifactId>
        <version>6.2</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.deltaspike.core</groupId>
        <artifactId>deltaspike-core-impl</artifactId>
        <version>version</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.deltaspike.core</groupId>
        <artifactId>deltaspike-core-api</artifactId>
        <version>version</version>
    </dependency>
    
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-data-module-api</artifactId>
        <version>version</version>
        <scope>compile</scope>
    </dependency>
    
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-data-module-impl</artifactId>
        <version>version</version>
        <scope>compile</scope>
    </dependency>

    Of cource you can override them in your pom.xml as needed.

  2. 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.
  3. 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;
        }
    }
  4. 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 gain equals(),hashCode() and toString().
  5. 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.
  6. 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)
        }
    
    }
    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:

xhtml page
 <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:

CarService
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;
    }
ℹ️

filter.params is a hashmap used to add arbitrary parameters and filter.entity is for entity specific ones, see search dialog which populates those attributes:

<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 Session so when user goes to another page (e.g a detail) and comes back to list the tables keeps it’s previous filters.

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>
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.