Showing posts with label querydsl. Show all posts
Showing posts with label querydsl. Show all posts

Monday, December 19, 2011

Productivity by Example: Liferay reverse-engineered

In this experiment, minuteproject demonstrates that it is possible to use Reverse-Engineering as a development methodology for Big Projects AND this from bootstrap time to the entire lifecycle of a Project.

To illustrate this postulat, I take enterprise-size opensource model (Liferay with150+ entities) upon which I apply minuteproject tracks.
It will quickly appear that performing 'bulk reverse-engineering' have strong limitations. Meanwhile by applying conventions deduced from the 'Reverse-Analysis' discipline, it will be possible to overcome them.
The generation will run releasing in couple of seconds (3 to 10) hundreds of artefacts.
Following Minuteproject philosophy, you'll will have to write 0 (Zero) LOC to get a working result and this can be achieved within 5 min-.
Eventually some code will be written on top and the result will be tested.
The technologies discussed will be:
  • JPA2 with Hibernate implementation
  • Querydsl
  • Ehcache 
  • Fitnesse
But other MP tracks can be applied.
The conclusion resumes the productivity benefit at project and enterprise level of embracing MinuteProject productivity philosophy.

Liferay
Liferay is an opensource portal, this article only focus on the database model.
Liferay Model
Setup 
The model is install on mysql database.
Now you can introspect your model.

Reverse-Analysis crash course
At first sight 150+ tables, 0 views.
Some tables start with QUARTZ, those are likely to be shipped with the Quartz scheduling framework, so they should be excluded from the generation.

At second sight (on mysql installation)
Entity with primary key but no foreign keys. => No relationships?
Mysql schema primary key are not autoincrement.

If reverse-engineering is performed as-is, then the bulk resulting artefacts in JPA2 will contain no relationships (no @OneToMany, @ManyToOne and @ManyToMany), so the resulting value will be quite poor (no graph navigation, query link to be done manually, no type safe benefit of Querydsl or Metamodel...)

Relation detection convention
Since the relationships are not formalised in terms of constraint, does it mean that there is none?
In fact, when having a deeper look inside the tables, some fields have name such as USER_ID and there is a strong chance that they are used for a relationship link with the User_ table primary key.
Let's assume it.
Since in minuteproject configuration the user can perform some enrichment and one of this enrichment consists in defining relationship, it is possible to enrich every table containing the field USER_ID by indicating it acts as a foreign key towards USER_ table.
// add snippet
It's fine, but it might be cumbersome since you'll have to do it more than 40 times and only for the 'towards User' relationships. What about the others?
A wiser way is to apply a convention:
What we need is to be able to detect relationships when those match a pattern.The pattern here is that the 'considered' foreign keys are fields ending with 'id' and whose beginning (column name without ending 'id') correspond to an existing table.
But this was not enough since some table ends with '_'.
So the convention has to take care of those exceptions and the match is done against the a map of those entity alias if the match was not successful via direct binding.
The final configuration of the convention is

<foreign-key-convention type="autodetect-foreign-key-based-on-similarity-and-map" 
column-ending="id" column-starting="" column-body="match-entity">
<property tag="map-entity" name="user_" value="user"/> 
<property tag="map-entity" name="group_" value="group"/>
<property tag="map-entity" name="organization_" value="organization"/>
<property tag="map-entity" name="permission_" value="permission"/>
</foreign-key-convention>

With this convention it is now possible to have 'virtual' foreign keys used by the generator to create @OneToMany, @ManyToOne and @ManyToMany relationships for JPA2 track.

Naming convention
The JEE conventions can be different from those retrieved from the Database structure:
Example: Some column ends with 'id' but in java we might not want that.
Here the convention for that
<column-naming-convention type="apply-strip-column-name-suffix" pattern-to-strip="ID" />

Collection naming convention
While reverse-engineering a problem is to give unambiguous names to list elements. Otherwise in java you have a compilation error. The safest way is to compose the name of this collection relationship (@OneToMany) with the name of the field holding the foreign key, and the name of entity having this FK. It get even worse when dealing with a many2many relationship where the intermediary link table as to be associated.
As a result, you can have very long name which is not quite handy.
Fortunately the convention apply-reference-alias-when-no-ambiguity is there for you!
<reference-naming-convention type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
It checks if while reducing the 'unambiguous' default name to 'just' the associated table (plurialize as option) there is no colision.
Remark
With conventions bear in mind that they are applied sequentially and so the order is very important!
Aliasing 
Entity name or column name can be aliased.
The alias will be used for the Java name but the ORM mapping will match the alias name to the correct entity name. 
Caching
To enable caching add a property to the target JPA2
               <property name="add-cache-implementation" value="ehcache"></property>
It is possible to associate caching to entities by two means:
By enrichment at entity level: Each entity marked as having their content-type="master-data" or ="reference-data" will have an associate cache entry
<entity name="country" content-type="reference-data"></entity>
By convention: describe a pattern of entity matching the requirement. Example all the entity ending with '_' could be considered as reference data
<entity-content-type-convention type="apply-content-type-to-entity-ending-with" pattern="_" content-type="reference-data"></entity-content-type-convention>
In this case the entity 'user', 'account', 'classname', 'contact', 'group', 'lock', 'organisation', 'permission', 'release', 'resouce' and 'role' will match the convention and thus have their cache entry in ehcache.xml
MinuteProject input
Input configuration
The analysis is resumed in the mp-config_LR.xml serving as input of MinuteProject
<!DOCTYPE root>
<generator-config>
    <configuration>
        <conventions>
            <target-convention type="enable-updatable-code-feature" />
        </conventions>       
        <model name="liferay" version="1.0" package-root="net.sf.mp.demo">
            <data-model>
                <driver name="mysql" version="5.1.16" groupId="mysql" artifactId="mysql-connector-java"></driver>
                <dataSource>
                    <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
                    <url>jdbc:mysql://127.0.0.1:3306/lportal</url>
                    <username>root</username>
                    <password>mysql</password>
                </dataSource>
                <primaryKeyPolicy oneGlobal="true">
                    <primaryKeyPolicyPattern name="none"></primaryKeyPolicyPattern>
                </primaryKeyPolicy>
            </data-model>
            <business-model>   
                    <generation-condition>
                    <condition type="exclude" startsWith="QUARTZ"></condition>
                    </generation-condition>
                <business-package default="liferay">
                    <condition type="package" startsWith="social" result="social"></condition>
                    <condition type="package" startsWith="user" result="user"></condition>
                    <condition type="package" startsWith="journal" result="journal"></condition>               
                </business-package>
                <enrichment>
                    <conventions>
                        <column-naming-convention type="apply-strip-column-name-suffix" pattern-to-strip="ID" />
                        <foreign-key-convention type="autodetect-foreign-key-based-on-similarity-and-map"
                            column-ending="id" column-starting="" column-body="match-entity">
                            <property tag="map-entity" name="user_" value="user"/>
                            <property tag="map-entity" name="group_" value="group"/>
                            <property tag="map-entity" name="organization_" value="organization"/>
                            <property tag="map-entity" name="permission_" value="permission"/>
                        </foreign-key-convention>
                        <reference-naming-convention type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />       
                        <entity-content-type-convention type="apply-content-type-to-entity-ending-with" pattern="_" content-type="reference-data"></entity-content-type-convention>
                    </conventions>
                    <entity name="user_" alias="user"></entity>
                    <entity name="role_" alias="role"></entity>
                    <entity name="group_" alias="group"></entity>
                    <entity name="permission_" alias="permission"></entity>
                    <entity name="organization_" alias="organization"></entity>
                    <entity name="classname_" alias="classname"></entity>
                    <entity name="country" content-type="reference-data"></entity>
                </enrichment>
            </business-model>
        </model>
        <targets>
            <target refname="JPA2"
               fileName="mp-template-config-JPA2.xml"
               outputdir-root="../../dev/liferay/output/JPA2"
               templatedir-root="../../template/framework/jpa">
               <property name="add-querydsl" value="2.1.2"></property>
               <property name="add-jpa2-implementation" value="hibernate"></property>
               <property name="add-cache-implementation" value="ehcache"></property>
               
            </target>                        
            <target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
                templatedir-root="../../template/framework/bsla">
            </target>
    <target refname="CACHE-LIB" 
      fileName="mp-template-config-CACHE-LIB.xml" 
      templatedir-root="../../template/framework/cache">
    </target>            
        </targets>
    </configuration>
</generator-config>
Generation
Adapt the above config with the liferay database connection credentials.
You need version 0.7+ of minuteproject (and check that everything is fine by running the demos)
To generate place the following file mp-config_LR.xml into /mywork/config
Execute model-generation.(sh/cmd) mp-config_LR.xml

MinuteProject output
Generated artefacts
Entities
140+ entities have been created
12 Embeddable Id when composite primary have been found
Relationships
+- 300 OneToMany
+- 300 ManyToOne
+- 20 ManyToMany
Configuration
Entity Associate MetaModel for typesafe query
Maven pom.xml with querydsl integration
persistence.xml for test (local connection pool) and for release (jndi connection pool).
ehcache.xml

And All That in 10s less!
(local instance of mysql)


If you want to see what the resulting code looks like you can download on googlecode (version for minuteproject 0.8).
If you want to see what the Liferay datamodel looks like with relationships check here.


Test
But does it really work?
The sources are generated in /dev/liferay/output/JPA2
Writing a simple unit test in src/test/java which illustrates:
  • that you can write business added value
  • that querydsl for typesafe as an alternative to metamodel (MetaModel entities are also generated) is integrated
  • that ORM, ehcache configuration works
package my.liferay.test;

import java.util.List;

import javax.persistence.*;

import com.mysema.query.jpa.impl.JPAQuery;

import junit.framework.TestCase;
import net.sf.mp.demo.liferay.domain.liferay.Country;
import net.sf.mp.demo.liferay.domain.liferay.QCountry;

public class LiferayJPA2Test extends TestCase{
   
    public void testCountry() {
        // Initializes the Entity manager
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("liferay");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        //use Query dsl for type safe query
        JPAQuery query = new JPAQuery(em);
        QCountry qcountry = QCountry.country;
        List l = query.from(qcountry)
           .where(qcountry.countryid.lt(0))
           .list(qcountry);
        //remove the countries
        for (Country country:l) {
         em.remove(country);
        }
        // create a country
        Country c = new Country();
        // no primary policy key given so associate one manually 
        c.setCountryid(new Long(-100));
        c.setName("NEW-country");
        c.setA2("A2");
        c.setA3("A3");
        c.setActive(new Short("1"));
        c.setNumber("5");

        tx.begin();
        em.persist(c);
        tx.commit();

        em.close();
        emf.close();   
    }
}

Running with maven
Execute mvn package
This will compile and test the code.
In the build, querydsl classes will be generated and compiled
In the test, at run time, ORM and ehcache configuration will be loaded.

The sql statement issue by Hibernate at the end of the test is 
Hibernate:
    insert
    into
        country
        (a2, a3, active_, idd_, name, number_, countryId)
    values
        (?, ?, ?, ?, ?, ?, ?)
So yes, it works!
And package liferayBackEnd-1.0-SNAPSHOT.jar is created.

But what happens if there is a compile time or runtime issue with the generated code?
Do not worry the generated code is updatable which means that you can patch the code but your modification will be kept over successive generations. More information at http://minuteproject.wikispaces.com/Updatable_Generated_Code

Extend
Here the reverse-engineering target just one type of target JPA2 track.
Extend with other MinuteProject tracks
Example:
FitNessize you development: Adding the following snippet into the 'targets' node of the main configuration will generate an entire Fitnesse wiki ready to use for your acceptance testing.
   <target refname="FitNesse" 
      name="default" 
      fileName="mp-template-config-fitnesse.xml" 
      outputdir-root="../../dev/liferay/output/FITNESSE"
      templatedir-root="../../template/framework/fitnesse">
   </target>
For more information see this article
Extend you model
Enrich you model with:
  • views
  • store procedure
  • constraints
Minuteproject will generate relevant artefacts for them.
Conclusions
This article shows how to set reverse-engineering approach as a fast development methodology.
It is far less intrusive and restricted than Domain Driven Development and much faster.
This article also insists on how to apply your own conventions via 'declarative conventions'.

Minuteproject unleashes your productivity!
Things and tedious tasks that where bound to stay for the lifetime of your project can be removed.
At this point it takes more time for a DBA to write and execute an alter statement than to have full backend synchronisation!
Now you can move agile on your backend development.

Conclusion for the LifeRay Dev team:
Altering your backend might be quite long if you want to go to JPA2 track described above. Meanwhile you can use MinuteProject without any intrusivity on other tracks.
MinuteProject FitNesse fixture generated for Liferay and this really help increasing QA!
Try other MinuteProject tracks: for example any DB query can be RESTified (fully REST app generated) within seconds...

More information
More information about Minuteproject 4 JPA2 track can be found here.

Monday, June 27, 2011

MinuteProject 4 JPA2: Lazuly showcase

Intro
The goal of this article is to have a working JPA2 layer on top of a data model by writting 0 (yes ZERO) lines of code.
Lazuly-db is a opensource database hosted on googlecode that represents the structure of a database holding conference related informations.
Lazuly-db will be used as a showcase of various MinuteProject technology tracks.
In version 0.1 it has 13 tables, 2 views.

This article explains the different steps to quickly have a working JPA2 backend.
It is furthermore a tutorial on how to enrich the database model to get customized artifacts providing specific behaviour.
The purpose is to give to user enough material to customize it and go on extending your model.

MinuteProject short story
The idea behind minuteproject is to
  • go fast;
  • reduce the technology learning curve by knowledge cristalization;
  • improve the quality of the artifact;
  • shorten drastically the time to market of the product.
Minuteproject acts as a Productivity provider targeting multiple aspects of the reverse-engineering for a target architecture (here JPA2).
While most of existing reverse-engineering solution focus on one part of your architecture (Domain Object), MinuteProject can generate artifacts related to:
  • columns;
  • entities (tables,views);
  • stored procedures;
  • package;
  • model;
  • application.
It allows the developer to achieve 'reproductability' that is a great complement of reusability.
Reusability allows to bundle common behavior into a package/framework/specification in order to use them but not to code them.
Reproductability allows to reach the same quality of artifacts (config/classes...) matching a target architecture (reused) although the context changes (DB model).

JPA2 track
JPA2 is a JEE specification for persisting objects in a database.
MinuteProject 4 JPA2 (release 0.5.5) offers a reverse-engineering solution targeting JPA2 artifacts:
  • Java Entity classes for tables and views;
  • JPA2 metamodel;
  • persistence xml;
  • maven pom.xml;
  • vendor specific implementation;
  • querydsl integration.
More information can be found at minuteproject4jpa2

Operating principle
MinuteProject works with a configuration file.
This file holds the information regarding the model and how to enrich it. It also specifies the target bundle of templates for a technology track.

Datasource
Lazuly DB main entities: The schema below resumes the lazuly DB main entities (tables/views) and theirs relationships.


This part summarizes the different configurations used:

Here is the sample xml configuration that is applied to the model
<!DOCTYPE root>
<generator-config>
<configuration>
<model name="conference" version="1.0" package-root="net.sf.mp.demo">
<!-- <model name="lazuly" version="1.0" package-root="mp.demo"> -->
<data-model>
<driver name="mysql" version="5.1.16" groupId="mysql"
artifactId="mysql-connector-java"></driver>
<dataSource>
<driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
<url>jdbc:mysql://127.0.0.1:3306/conference</url>
<username>root</username>
<password>mysql</password>
</dataSource>
<!-- for Oracle and DB2 please set the schema <schema> </schema> -->
<primaryKeyPolicy oneGlobal="true">
<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
</primaryKeyPolicy>
</data-model>
<business-model>
<!-- <generation-condition> <condition type="exclude" startsWith="DUAL"></condition>
</generation-condition> -->
<business-package default="conference">
<condition type="package" startsWith="STAT" result="statistics"></condition>
<condition type="package" startsWith="COUNTRY" result="admin"></condition>
<condition type="package" startsWith="ROLE" result="admin"></condition>
</business-package>
<enrichment>
<conventions>
<column-naming-convention type="apply-strip-column-name-suffix"
pattern-to-strip="_ID" />
<reference-naming-convention
type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
</conventions>

<entity name="PRESENTATION">
<field name="STATUS">
<property tag="checkconstraint" alias="presentation_status">
<property name="PROPOSAL" value="PROPOSAL" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
</entity>
<entity name="SPONSOR">
<field name="STATUS">
<property tag="checkconstraint" alias="sponsor_status">
<property name="PENDING" value="PENDING" />
<property name="ACTIVE" value="ACTIVE" />
</property>
</field>
<field name="PRIVILEGE_TYPE">
<property tag="checkconstraint" alias="sponsor_privilege">
<property name="GOLDEN" value="Golden" />
<property name="SILVER" value="Silver" />
<property name="BRONZE" value="Bronze" />
</property>
</field>
</entity>
<!-- views -->
<entity name="stat_mb_per_ctry_conf" alias="MEMBER_PER_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="ID" />
</virtual-primary-key>
</entity>
<entity name="stat_mb_by_role" alias="MEMBER_PER_ROLE_COUNTRY_AND_CONFERENCE">
<virtual-primary-key isRealPrimaryKey="true">
<property name="virtualPrimaryKey" value="id" />
</virtual-primary-key>
<field name="stat_mb_per_ctry_conf_ID" linkToTargetEntity="stat_mb_per_ctry_conf"
linkToTargetField="id"></field>
</entity>
</enrichment>
</business-model>
</model>
<targets>

<target refname="JPA2" fileName="mp-template-config-JPA2.xml"
outputdir-root="../../dev/output/JPA2" templatedir-root="../../template/framework/jpa">
<property name="add-querydsl" value="2.1.2"></property>
<property name="add-jpa2-implementation" value="hibernate"></property>
</target>

<target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
templatedir-root="../../template/framework/bsla">
</target>

</targets>
</configuration>
</generator-config>
The configuration provides:
  • A model name: conference, version and package root;
  • the driver as maven reference;
  • connection parameters;
  • primarykey policy (autoincrement)
  • business packages: entities starting with stat go to 'statistics'...;role, countries goes in administration package
  • convention: strip-column-name-suffix _ID (by convention in the database, I have column naming ending with '_ID' for foreign key. Here the Java naming convention can defer from the DB naming
  • convention: apply-referenced-alias-when-no-ambiguity indicating that when a table has a one2many relationship with other (i.e. has child), if it has not 2 children with the same other side table (no ambiguity) than the name of the variable is based on the other side table, and plurialized (english) collections
  • Entity enrichment: name alias (can have a distinct Java class name than the one derived from the DB);
  • Field enrichment: provide a check constraint that becomes a Java Enum
  • Entity (views) enrichment by providing them a primary key (create a unique key) by adding foreignkey.
The target part of the configuration specifies against which target technology, MinuteProject has to generate. Here the track is JPA2, where it is specified to use querydsl 2.1.2 and a JPA vendor implementation Hibernate.
Those indications are used to generate pom.xml and persistence.xml files.

Sources and installation
Lazuly project
Svn the source as describe on http://code.google.com/p/lazuly/source/checkout

Mysql
Install mysql
run conference-db.0.1.sql on schema conference

Minuteproject
Download and install minuteproject 0.5.5 or higher. Minuteproject is installed in MP_HOME.

Execution
Normally within couple of seconds (1 to 5) the code should be generated (If DB instance and minuteproject run is on same machine)

Alternate execution
  • Open minuteproject console %MP_HOME%/bin/start-console.(cmd/sh)
  • field the connection parameters
  • add package root and model name
  • click generate
This alternative generation works fine to give a first glance at what you can have as output. A better enrichment is available via the xml config as previously described.

Output
The output can be browsed at http://code.google.com/p/lazuly/source/browse/#svn%2Ftrunk%2Flazuly-jpa2

Here are some samples of what has been generated in terms of

Structure
Here the structure open with as an eclipse-maven project


Java class as JPA2 entity with enumeration, OneToMany, ManyToOne, ManyToMany
You can note that the field 'abstract' in the table 'presentation' is converted to 'abstractName' to avoid compilation error. This is true for all the java reserved keywords. The getter and setter are adapted accordingly.
package net.sf.mp.demo.conference.conference;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import javax.persistence.*;

import net.sf.mp.demo.conference.conference.Evaluation;
import net.sf.mp.demo.conference.conference.PresentationPlace;
import net.sf.mp.demo.conference.conference.Speaker;
import net.sf.mp.demo.conference.enumeration.conference.PresentationStatusEnum;

/**
*
* <p>Title: Presentation</p>
*
* <p>Description: Domain Object describing a Presentation entity</p>
*
*/
@Entity (name="Presentation")
@Table (name="presentation")
public class Presentation {

@Id @Column(name="id" )
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name="start_time", nullable=false, unique=false)
private Timestamp startTime;
@Column(name="end_time", nullable=false, unique=false)
private Timestamp endTime;
@Column(name="abstract", length=500, nullable=false, unique=false)
private String abstractName;
@Column(name="title", length=255, nullable=false, unique=false)
private String title;
@Enumerated (EnumType.STRING)
private PresentationStatusEnum status;
@Column(name="proposal_time", nullable=true, unique=false)
private Timestamp proposalTime;

@ManyToOne (fetch=FetchType.LAZY )
@JoinColumn(name="presentation_place_id", referencedColumnName = "id", nullable=true, unique=false )
private PresentationPlace presentationPlaceId;

@OneToMany (targetEntity=net.sf.mp.demo.conference.conference.Evaluation.class, fetch=FetchType.LAZY, mappedBy="presentationId", cascade=CascadeType.REMOVE)//, cascade=CascadeType.ALL)
private Set <Evaluation> evaluations = new HashSet<Evaluation>();

@ManyToMany
@JoinTable(name="SPEAKER_PRESENTATION",
joinColumns=@JoinColumn(name="presentation_id"),
inverseJoinColumns=@JoinColumn(name="speaker_id")
)
private Set <Speaker> speakers = new HashSet <Speaker> ();

/**
* Default constructor
*/
public Presentation() {
}

public Long getId() {
return id;
}

public void setId (Long id) {
this.id = id;
}

public Timestamp getStartTime() {
return startTime;
}

public void setStartTime (Timestamp startTime) {
this.startTime = startTime;
}

public Timestamp getEndTime() {
return endTime;
}

public void setEndTime (Timestamp endTime) {
this.endTime = endTime;
}

public String getAbstractName() {
return abstractName;
}

public void setAbstractName (String abstractName) {
this.abstractName = abstractName;
}

public String getTitle() {
return title;
}

public void setTitle (String title) {
this.title = title;
}

public PresentationStatusEnum getStatus() {
return status;
}

public void setStatus (PresentationStatusEnum status) {
this.status = status;
}

public Timestamp getProposalTime() {
return proposalTime;
}

public void setProposalTime (Timestamp proposalTime) {
this.proposalTime = proposalTime;
}

public PresentationPlace getPresentationPlaceId () {
return presentationPlaceId;
}

public void setPresentationPlaceId (PresentationPlace presentationPlaceId) {
this.presentationPlaceId = presentationPlaceId;
}

public Set<Evaluation> getEvaluations() {
if (evaluations == null){
evaluations = new HashSet<Evaluation>();
}
return evaluations;
}

public void setEvaluations (Set<Evaluation> evaluations) {
this.evaluations = evaluations;
}

public void addEvaluations (Evaluation evaluation) {
getEvaluations().add(evaluation);
}

public Set<Speaker> getSpeakers() {
if (speakers == null){
speakers = new HashSet<Speaker>();
}
return speakers;
}

public void setSpeakers (Set<Speaker> speakers) {
this.speakers = speakers;
}

public void addSpeakers (Speaker speakers) {
getSpeakers().add(speakers);
}
}

Enumeration
You can note the 'equals (string s)' method which is quite handy.
package net.sf.mp.demo.conference.enumeration.conference;

import java.util.ArrayList;
import java.util.List;

public enum SponsorPrivilegeTypeEnum {

GOLDEN("Golden"),
SILVER("Silver"),
BRONZE("Bronze");

private final String value;

SponsorPrivilegeTypeEnum(String v) {
value = v;
}

public String value() {
return value;
}


public static SponsorPrivilegeTypeEnum fromValue(String v) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.value.equals(v)) {
return c;
}
}
return null;
}


/**
* Return a list that contains all the enumeration values
* @return List<SponsorPrivilegeTypeEnum> the that contains all the enumeration values
*/
public static List<SponsorPrivilegeTypeEnum> getList() {
List<SponsorPrivilegeTypeEnum> list = new ArrayList<SponsorPrivilegeTypeEnum>();
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
list.add(c);
}
return list;
}

/**
* @param value
* @return boolean : true if the value exist in the enum
*/
public static boolean contains (String value) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.toString().equals(value))
return true;
}
return false;
}

public static boolean containsValue (String value) {
for (SponsorPrivilegeTypeEnum c : SponsorPrivilegeTypeEnum.values()) {
if (c.value().equals(value))
return true;
}
return false;
}

public boolean equals (String s) {
if (s==null) return false;
return s.equals(value);
}

}
Java class as JPA2 entity for view
You can note the enrichment performed is translated into different Java and DB naming conventions for the entity itself as well as its dependencies.
Now you can use the view for all the JPA2 select operations.
package net.sf.mp.demo.conference.statistics;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import javax.persistence.*;

import net.sf.mp.demo.conference.statistics.MemberPerRoleCountryAndConference;

/**
*
* <p>Title: MemberPerCountryAndConference</p>
*
* <p>Description: Domain Object describing a MemberPerCountryAndConference entity</p>
*
*/
@Entity (name="MemberPerCountryAndConference")
@Table (name="stat_mb_per_ctry_conf")
public class MemberPerCountryAndConference {

@Id @Column(name="id" ,length=300)
private String id;

@Column(name="country", length=45, nullable=false, unique=false)
private String country;
@Column(name="conference_name", length=255, nullable=false, unique=false)
private String conferenceName;
@Column(name="number", nullable=false, unique=false)
private Long number;

@OneToMany (targetEntity=net.sf.mp.demo.conference.statistics.MemberPerRoleCountryAndConference.class, fetch=FetchType.LAZY, mappedBy="statMbPerCtryConfId", cascade=CascadeType.REMOVE)//, cascade=CascadeType.ALL)
private Set <MemberPerRoleCountryAndConference> memberPerRoleCountryAndConferences = new HashSet<MemberPerRoleCountryAndConference>();

/**
* Default constructor
*/
public MemberPerCountryAndConference() {
}

public String getId() {
return id;
}

public void setId (String id) {
this.id = id;
}

public String getCountry() {
return country;
}

public void setCountry (String country) {
this.country = country;
}

public String getConferenceName() {
return conferenceName;
}

public void setConferenceName (String conferenceName) {
this.conferenceName = conferenceName;
}

public Long getNumber() {
return number;
}

public void setNumber (Long number) {
this.number = number;
}

public Set<MemberPerRoleCountryAndConference> getMemberPerRoleCountryAndConferences() {
if (memberPerRoleCountryAndConferences == null){
memberPerRoleCountryAndConferences = new HashSet<MemberPerRoleCountryAndConference>();
}
return memberPerRoleCountryAndConferences;
}

public void setMemberPerRoleCountryAndConferences (Set<MemberPerRoleCountryAndConference> memberPerRoleCountryAndConferences) {
this.memberPerRoleCountryAndConferences = memberPerRoleCountryAndConferences;
}

public void addMemberPerRoleCountryAndConferences (MemberPerRoleCountryAndConference memberPerRoleCountryAndConference) {
getMemberPerRoleCountryAndConferences().add(memberPerRoleCountryAndConference);
}

}

Run
Since it generate a pom.xml, download maven and run 'mvn install'
It generates the lazuly backend jar containing the entities, the metamodel and the querydsl classes.