С помощью современных ORM можно легко создавать общие DAO. Все, что нам нужно от IOC, — это возможность различать две службы (реализующие один и тот же интерфейс) по их универсальному типу. Проблема заключается в том, что tapestry-ioc может различать две службы на основе идентификатора службы или аннотации маркера, а не по универсальному типу. Итак, если у нас есть общая реализация DAO, мы не можем сделать что-то вроде
@Inject
private EntityDAO<User> userDAO;
(Обратите внимание, я использую термин EntityDAO для общего дао.) Одним из решений будет что-то вроде
@InjectDAO(User.class)
private EntityDAO<User> userDAO;
Еще один может быть
@ServiceId("UserDAO")
@Inject
private EntityDAO<User> userDAO;
Последнее может быть реализовано, если мы сможем представить наш DAO как сервис. Первый может быть реализован в терминах последнего с использованием преобразований классов.
Второй вопрос — как зарегистрировать DAO для каждого объекта. Это можно сделать вручную, как
static public EntityDAO<User> buildUserDAO(){
return new EntityDAOImpl(User.class);
}
Но представьте, что вы делаете это для базы данных с сотнями сущностей (особенно, если вы ленивый пользователь гобелена). Решением будет загрузка объектов при запуске и создание соответствующих DAO. Это было бы замечательно, но гобелен не обеспечивает динамичный способ предоставления новых услуг. Да, вы можете добавить ObjectProvider в MasterObjectProvider, но с этим вы создаете объект, а не сервис. Некоторые из функций, которые вы теряете, — это возможность напрямую применять советы к вашим услугам и невозможность использования аннотации @InjectService.
Итак, вот трюк для динамического создания сервиса. Мы создаем новое определение модуля, которое принимает список определений сущности в качестве аргумента и создает определение сервиса для каждого. Определение сервиса не создает экземпляр сервиса, а делегирует его другому сервису EntityDAOSource. Это может быть сервис по построению цепочек, в который вы можете легко добавлять услуги.
Итак, вот наше определение модуля
public class EntityModuleDef implements ModuleDef {
private Map<String, ServiceDef> serviceDefs = new HashMap<String, ServiceDef>();
public EntityModuleDef(EntityLocator locator){
for(EntityDef entityDef: locator.getEntityDefs()){
serviceDefs.put(entityDef.getServiceId(), new EntityDAOServiceDef(entityDef));
}
}
public Set<String> getServiceIds() {
return serviceDefs.keySet();
}
public ServiceDef getServiceDef(String serviceId) {
return serviceDefs.get(serviceId);
}
public Set<DecoratorDef> getDecoratorDefs() {
return CollectionFactory.newSet();
}
public Set<ContributionDef> getContributionDefs() {
return CollectionFactory.newSet();
}
@SuppressWarnings("rawtypes")
public Class getBuilderClass() {
return null;
}
public String getLoggerName() {
return EntityModuleDef.class.getName();
}
}
Интерфейс EntityLocator предоставляет определения сущностей.
//Entity definition
public interface EntityDef {
//Get the service id
String getServiceId();
//Get the entity type
Class<?> getType();
}
public interface EntityLocator {
//Gets the list of entity definitions
Set<EntityDef> getEntityDefs();
}
Простая реализация EntityLocator будет:
public abstract class SimpleEntityLocator implements EntityLocator {
private Set<EntityDef> entityDefs = new HashSet<EntityDef>();
public AbstractEntityLocator(Set<String> packageNames){
ClassNameLocator locator = new ClassNameLocatorImpl(new ClasspathURLConverterImpl());
for(String packageName: packageNames){
for(String className: locator.locateClassNames(packageName)){
try {
final Class<?> entityClass = Class.forName(className);
if(isEntity(entityClass)){
entityDefs.add(new EntityDef(){
public String getServiceId() {
return entityClass.getSimpleName() + "DAO";
}
public Class<?> getType() {
return entityClass;
}
@Override
public String toString(){
return "Entity Definition for " + getServiceId();
}
});
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
public boolean isEntity(Class<?> entityClass){
return entityClass.getAnnotation(javax.persistence.Entity.class) != null ||
entityClass.getAnnotation(javax.persistence.MappedSuperClass.class) != null;
}
public Set<EntityDef> getEntityDefs() {
return entityDefs;
}
}
Определение сервиса
public class EntityDAOServiceDef implements ServiceDef {
private EntityDef entityDef;
public EntityDAOServiceDef(EntityDef entityDef) {
this.entityDef = entityDef;
}
public ObjectCreator createServiceCreator(final ServiceBuilderResources resources) {
return new ObjectCreator() {
public Object createObject() {
Object object = resources.getService(EntityDAOSource.class).get(entityDef.getType());
if(object == null){
throw new EntityDAONotFoundException(
"Could not find EntityDAO implementation for " + entityDef.getType());
}
return object;
}
};
}
public String getServiceId() {
return entityDef.getServiceId();
}
@SuppressWarnings("rawtypes")
public Set<Class> getMarkers() {
return CollectionFactory.newSet();
}
@SuppressWarnings("rawtypes")
public Class getServiceInterface() {
return EntityDAO.class;
}
public String getServiceScope() {
return ScopeConstants.DEFAULT;
}
public boolean isEagerLoad() {
return false;
}
}
Определение сервиса делает все, кроме фактического создания сервиса, который делегирован EntityDAOSource.
В качестве примера рассмотрим общий DAO, реализованный в Hibernate.
public interface EntityDAO<E> {
List<E> list();
void save(E entity);
void saveOrUpdate(E entity);
void update(E entity);
void remove(E entity);
int count();
E find(Serializable id);
}
public class HibernateEntityDAOImpl<T> implements EntityDAO<T> {
private Class<?> type;
public HibernateEntityDAOImpl(Class<T> type) {
this.type = type;
}
protected Session getSession() {
return //Get current session...
}
public T find(Serializable id) {
return (T) getSession().get(type, id);
}
public void save(T entity) {
getSession().save(entity);
}
public void update(T entity) {
getSession().update(entity);
}
public void remove(T entity) {
getSession().delete(entity);
}
public void saveOrUpdate(T entity) {
getSession().saveOrUpdate(entity);
}
public List<T> list(){
return (List<T>)getSession().createCriteria(type).query();
}
public int count(){
return (int)getSession().createCriteria(type).setProjection(Projections.rowCount()).uniqueResult();
}
}
Для этой реализации EntityDAOSource может быть реализован как
public class HibernateEntityDAOSource implements EntityDAOSource {
public <E> EntityDAO<E> get(Class<E> entityClass) {
return new HibernateEntityDAOImpl<E>(type);
}
}
Наконец, это определение модуля может быть передано путем переопределения метода provideExtraModuleDefs () в TapestryFilter.
public class TapestryTawusFilter extends TapestryFilter {
private static final String MODEL_PACKAGES = "tawus-model-packages";
@Override
protected ModuleDef [] provideExtraModuleDefs(ServletContext context){
String packages = context.getInitParameter(MODEL_PACKAGES);
if(packages == null){
return new ModuleDef[]{};
}
EntityLocator entityLocator = new AbstractEntityLocator(CollectionFactory.newSet(
TapestryInternalUtils.splitAtCommas(packages))){
@SuppressWarnings("unchecked")
public boolean isEntity(@SuppressWarnings("rawtypes") Class entityType){
return entityType.getAnnotation(Entity.class) != null;
}
};
return new ModuleDef[]{ new EntityModuleDef(entityLocator)};
}
}
От http://tawus.wordpress.com/2011/05/28/tapestry-magic-13-generic-data-access-objects/