Могу ли я сделать эту универсальную вещь? - PullRequest
4 голосов
/ 18 ноября 2009

Кажется, я что-то упустил в Java Generics, потому что что-то, на мой взгляд, простое, мне кажется, что это невозможно. Может быть, вы можете помочь ...

Это сценарий: я кодирую общий абстрактный DAO с простой операцией CRUD, чтобы каждый конкретный DAO моего приложения мог иметь его бесплатно:

public abstract DefaultDAO<T,V> {
  private EntityManager manager;

  public BaseDAO(EntityManager em) {
    this.manager = em;
  }

  public void create(T entity) {
    manager.persist(entity);
  }

  // You know the others...

  public T read(V pk) {
    // Now, here is the problem. 
    // EntityManager signature is: public <T> T find(Class<T> entityClass, Object primaryKey);
    // So I must provide the type of the object this method will be returning and 
    // the primary key.
    // resulting object will be T typed and pk type is V type (although is not needed to type it since the method expects an Object)
    // So... I expected to be allowed to do something like this
    return manager.find(T, pk); // But it's not allowed to use T here. T is not an instance
  }   
}

Теперь я бы пошел и внедрил конкретный DAO:

public PersonDAO extends DefaultDAO<PersonEntity, Long> {
  public PersonDAO(EntityManager em) {
    super(em);
  }
  // CRUD methods are inherited
}

И код клиента для моего DAO будет:

EntityManager manager = ...
PersonDAO dao = new PersonDAO(manager);
Long pk = .....
PersonEntity person = dao.find(pk); // DAO would return a PersonEntity

Когда клиент выполняет код, BaseDAO знает тип сущности, которую он должен вернуть, и тип первичного ключа этой сущности, потому что я установил его для определенного dao, но я не знаю, как кодировать метод read () правильно.

Надеюсь, вы сможете помочь. Большое спасибо!

Ответы [ 5 ]

14 голосов
/ 18 ноября 2009

Вы пытаетесь использовать параметр типа, как будто это нормальное выражение.Вы не можете сделать это.В других языках вы можете получить класс для параметра типа во время выполнения, но вы не можете получить в Java из-за стирание типа .

В этом случае вам понадобитсяпередать Class<T> во время выполнения - например, конструктору:

public abstract class DefaultDAO<T, V> {

    private EntityManager manager;
    private final Class<T> clazz;

    public DefaultDAO(EntityManager em, Class<T> clazz) {
        this.manager = em;
        this.clazz = clazz;
    }

    public T read(V pk) {
        return manager.find(clazz, pk);
    }
}
3 голосов
/ 23 мая 2014

Я думаю, вы можете сделать это:

 public <T> T find(Class<T> clazz, Object id) {
        return entityManager.find(clazz, id);
    }
3 голосов
/ 19 ноября 2009

Существует способ сделать это с помощью рефлексии, если ваши классы следуют согласованной иерархии классов в терминах обобщенных типов (т.е. любые промежуточные классы в иерархии наследования между вашим базовым классом и вашим конкретным классом используют одни и те же универсальные параметры в в том же порядке).

Мы используем что-то подобное в нашем абстрактном классе, определенном как HibernateDAO, где T - тип сущности, а K - PK:

private Class getBeanClass() {
    Type daoType = getClass().getGenericSuperclass();
    Type[] params = ((ParameterizedType) daoType).getActualTypeArguments();
    return (Class) params[0];
}

, который немного пахнет, но обменивает неряшливость передачи .class по сравнению с конкретной реализацией в конструкторе на лукавость, настаивая на том, что вы сохраняете иерархию типов таким же образом.

3 голосов
/ 18 ноября 2009

Java больше не имеет общей информации о классе во время выполнения (это называется Type Erasure). Что я сделал, так это дал моему Abstract Daos экземпляр универсального класса.

public abstract DefaultDAO<T,V> {

  protected Class<T> genericClass;
  private EntityManager manager;

  protected BaseDAO(EntityManager em, Class<T> implclass) {
    this.manager = em;
  }

  public void create(T entity) {
    manager.persist(entity);
  }

  // You know the others...

  public T read(V pk) {
    return manager.find(this.getGenericClass(), pk); // But it's not allowed to use T here. T is not an instance
  }

  public Class<T> getGenericClass()
  {
    return genericClass;
  }
}

public class Blubb
{
    private String id;

    // Getters and Stuff...
}

public class BlubbDao extends DefaultDAO<Blubb, String>
{
    public BlubbDao(EntityManager em)
    {
        super(em, Blubb.class);
    }
}

Я не могу обещать, что он выйдет из коробки, но я надеюсь, что вы поняли идею.

2 голосов
/ 18 ноября 2009

Эта статья делает то, что вам нужно. http://blog.xebia.com/2009/03/09/jpa-implementation-patterns-data-access-objects/

...