2011-02-03 15 views
9

Tengo una clase DAO abstracta que usa tipos parametrizados E (Entidad) y K (Clave principal). En cada entidad tengo un @NamedQuery. Quiero invocar dinámicamente esta consulta con nombre sin saber su nombre exacto y el nombre del parámetro.Resumen de consultas con nombre en un resumen JPA DAO

Como ejemplo, imaginemos la siguiente entidad City

@Entity(name="CITY") 
@NamedQuery(
    name="findCityByname", 
    query="FROM CITY c WHERE name = :CityName" 
) 
public class City { 
    // ... 
} 

y esto CityDao

public class CityDao extends AbstractDao<City, Long> { 
    public CityDao() { 
     super(City.class); 
    } 
} 

¿Cómo debo aplicar el método findByName() en AbstractDao modo que no necesito saber la exacta nombre y nombre del parámetro?

public abstract class AbstractDao<E, K> implements Dao<E, K> { 

    @PersistenceContext 
    protected EntityManager entityManager; 
    protected Class<E> entityClass; 

    protected AbstractDao(Class<E> entityClass) { 
     this.entityClass = entityClass; 
    } 

    @Override 
    public E findByName(String name) { 
     try { 
      return (E) entityManager 
       .createNamedQuery("findCityByName") 
       .setParameter("CityName", name) 
       .getSingleResult(); 
     } catch(Exception e) { 
      return null; 
     } 
    } 

    // ... 
} 

Respuesta

11

La convención de nomenclatura para consultas con nombre suele ser <Entity Name>.findBy<PropertyAndAnotherProperty>, "City.findByName" en su ejemplo, por lo que trataría de cambiar las consultas con nombre para seguir este patrón. El parámetro para esta consulta también debería tener el mismo nombre, o podría usar parámetros posicionales. Su método find entonces se convierten en

@Override 
public E findByName(String name) { 
    E entity = null; 
    try { 
     return (E)entityManager.createNamedQuery(myClass.getSimpleName() + ".findByName") 
           .setParameter("name", name) 
           .getSingleResult(); 
    } catch (Exception ex) { 
     return null; 
    } 
} 
1

El método más simple es pasar el nombre de la consulta al constructor de la DAO abstracta:

public DaoAbstreact(Class myClass, String findByNameQueryName) { 
    this.myClass = myClass; 
    this.findByNameQueryName = findByNameQueryName; 
} 

A continuación, definir una cadena public static final en la ciudad para contener el nombre:

public class ConcreteCityDao<City,Long> extends DaoAbstreact {  
    ConcreteCityDao(){ 
     super(City.class, City.FIND_BY_NAME_QUERY_NAME)); 
    } 
} 

como alternativa puede declarar DaoAbstreact tan abstracto y luego tener un método como este en ella:

public abstract String getFindByNameQueryName(); 

E implemente eso en ConcreteCityDao.

Por último también se puede introducir una enumeración:.

public enum NamedEntityType { 
    CITY(City.class, "findCityByname"), 
    PERSON(Person.class, "findPersonByname"); 

    private final Class<?> entityClass; 

    private final String findByNameQueryName; 

    private NamedEntityType(Class<?> entityClass, String findByNameQueryName) { 
     this.entityClass = entityClass; 
     this.findByNameQueryName = findByNameQueryName; 
    } 

    public Class<?> getEntityClass() { 
     return entityClass; 
    } 

    public String getFindByNameQueryName() { 
     return findByNameQueryName; 
    } 
} 

Luego, su DAO puede determinar el tipo de la clase aprobada en Para asegurar que no se olvide de añadir una entidad a la enumeración que puede hacer cada uno entidad implementa una interfaz con un método getNamedEntityType(). Luego puede especificar que su DAO genérico abstracto solo aceptará entidades que implementen esa interfaz.

+0

que está golpeando el concepto 'yo quiero vivir con el concepto de tipo de mota ... –

0

La forma más obvia sería la de pasar valores de las clases concretas a la superclase abstracta usando abstract método

public abstract class AbstractDao<E, K extends Serializable> implements Dao <E, K> { 
    ... 
    protected abstract String getFindByNameQueryName(); 

    @Override 
    public E findByName(String EntityStr) { 
     ... entityManager.createNamedQuery(getFindByNameQueryName()) ... 
    } 
} 

@Override 
public class ConcreteCityDao<City,Long> extends DaoAbstreact{ 
    ... 
    protected String getFindByNameQueryName() { 
     return "findCityByName"; 
    } 
} 

o como un argumento del constructor:

public abstract class AbstractDao<E, K extends Serializable> implements Dao<E, K> { 
    public AbstractDao(Class<E> myClass, String findByNameQueryName) { ... } 
    ... 
} 

@Override 
public class ConcreteCityDao<City, Long> extends DaoAbstreact{ 
    public ConcreteCityDao() { 
     super(City.class, "findCityByName"); 
    } 
} 

Aunque esto requiere de nomenclatura coherente de los parámetros de consulta para diferentes entidades.

También tenga en cuenta las pequeñas mejoras en estos fragmentos.

0

Lo que parece básicamente a querer es para anotar las anotaciones que definen las consultas con nombre, de tal manera que se puede mediante programación descubrir lo que la consulta "findByName" es (y posible otras consultas).

Dado que esto no es posible en Java, puede usar el hecho de que @NamedQuery admite sugerencias de consulta, que se definen como específicas del proveedor. Los consejos desconocidos son ignorados. Se podría añadir sus propios datos aquí, que la DAO genérico puede leer de nuevo desde entityClass:

@NamedQuery(
    name="findCityByname", 
    query="FROM CITY c WHERE name = :CityName", 
    [email protected](name="genericDAO.type", value="findByName") 
)