Подтвердить что ты не робот

Как сохранить чистое разделение слоев с помощью Hibernate/ORM?

Как можно сохранить чистые слои с помощью Hibernate/ORM (или других ORM...)?

То, что я подразумеваю под чистым разделением слоев, заключается в том, чтобы сохранить все Hibernate в DAO.

Например, при создании большого экспортного потока

4b9b3361

Ответ 1

Если под "чистым" вы подразумеваете, что верхние слои не знают о реализации нижних слоев, вы можете обычно применять Скажите, не спрашивайте принцип. Для вашего примера потоковой передачи CSV это будет что-то вроде:

// This is a "global" API (meaning it is visible to all layers). This is ok as
// it is a specification and not an implementation.
public interface FooWriter {
    void write(Foo foo);
}

// DAO layer
public class FooDaoImpl {
    ...
    public void streamBigQueryTo(FooWriter fooWriter, ...) {
        ...
        for (Foo foo: executeQueryThatReturnsLotsOfFoos(...)) {
            fooWriter.write(foo);
            evict(foo);
        }
    } 
    ...
}

// UI layer
public class FooUI {
    ...
    public void dumpCsv(...) {
        ...
        fooBusiness.streamBigQueryTo(new CsvFooWriter(request.getOutputStream()), ...);
        ...
    }
}

// Business layer
public class FooBusinessImpl {
    ...
    public void streamBigQueryTo(FooWriter fooWriter, ...) {
        ...
        if (user.canQueryFoos()) {
            beginTransaction();
            fooDao.streamBigQueryTo(fooWriter, ...);
            auditAccess(...);
            endTransaction();
        }
        ...
    }
}

Таким образом, вы можете иметь дело со своей конкретной ORM со свободой. Недостаток этого подхода "обратного вызова": если ваши слои находятся на разных JVM, тогда это может быть не очень эффективно (в примере вам нужно будет сериализовать CsvFooWriter).

Об общих DAO: я никогда не чувствовал необходимости, большинство шаблонов доступа к объектам, которые я нашел, достаточно различны, чтобы сделать желаемую конкретную реализацию. Но, конечно, разделение слоев и форсирование бизнес-уровня для создания критериев Hibernate являются противоречивыми путями. Я хотел бы указать другой метод запроса на уровне DAO для каждого другого запроса, а затем я бы позволил реализации DAO получить результаты любым способом, который он мог бы выбрать (критерии, язык запросов, необработанный SQL,...). Поэтому вместо:

public class FooDaoImpl extends AbstractDao<Foo> {
    ...
    public Collection<Foo> getByCriteria(Criteria criteria) {
        ...
    }
}

public class FooBusinessImpl {
    ...
    public void doSomethingWithFoosBetween(Date from, Date to) {
        ...
        Criteria criteria = ...;

        // Build your criteria to get only foos between from and to

        Collection<Foo> foos = fooDaoImpl.getByCriteria(criteria);
        ...
    }

    public void doSomethingWithActiveFoos() {
        ...
        Criteria criteria = ...;

        // Build your criteria to filter out passive foos

        Collection<Foo> foos = fooDaoImpl.getByCriteria(criteria);
        ...
    }
    ...
}

Я бы сделал:

public class FooDaoImpl {
    ...
    public Collection<Foo> getFoosBetween(Date from ,Date to) {
        // build and execute query according to from and to
    }

    public Collection<Foo> getActiveFoos() {
        // build and execute query to get active foos
    }
}

public class FooBusinessImpl {
    ...
    public void doSomethingWithFoosBetween(Date from, Date to) {
        ...      
        Collection<Foo> foos = fooDaoImpl.getFoosBetween(from, to);
        ...
    }

    public void doSomethingWithActiveFoos() {
        ...
        Collection<Foo> foos = fooDaoImpl.getActiveFoos();
        ...
    }
    ...
}

Хотя кто-то может подумать, что я подталкиваю некоторую бизнес-логику до уровня DAO, это кажется лучшим подходом ко мне: изменить реализацию ORM на альтернативу было бы проще. Представьте себе, например, что по соображениям производительности вам нужно читать Foo с использованием необработанного JDBC для доступа к определенному расширению поставщика: с помощью общего подхода DAO вам нужно будет изменить как бизнес, так и уровни DAO. При таком подходе вы просто переопределяете уровень DAO.

Ответ 2

Хорошо, вы всегда можете сказать своему слою DAO, что он должен делать, когда захотите. Имея метод, подобный cleanUpDatasourceCache в вашем DAO-слое, или что-то подобное (или даже набор этих методов для разных объектов), для меня не так уж плохо.

И ваш уровень обслуживания затем сможет вызвать этот метод без каких-либо предположений о том, что сделано DAO под капотом. Конкретная реализация, которая использует прямые вызовы JDBC, ничего не сделает в этом методе.

Ответ 3

Обычно необходим слой DAO для переноса логики доступа к данным. Другие времена - это просто EntityManager, что вы хотите использовать для операций CRUD, для этих случаев я бы не использовал DAO, поскольку это добавило бы лишнюю сложность в код.

Как следует использовать EntityManager в хорошо отделенном слое услуг и уровне доступа к данным?

Ответ 4

Если вы не хотите связывать свой код с Hibernate, вы можете использовать Hibernate через JPA и не слишком беспокоиться об абстрагировании всего в ваших DAO. Вы с меньшей вероятностью переключаетесь с JPA на что-то другое, вместо замены Hibernate.

Ответ 5

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

  • Ваш DAO должен повторно использоваться в другом контексте, кроме экспортировать данные csv?

  • имеет смысл иметь другую реализацию того же DAO интерфейс без спящего режима?

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

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

Ответ 6

вы можете добиться разделения слоев путем реализации шаблона DAO и выполнения всех связанных с hibernate/JDBC/JPA элементов в самом Dao

например:

вы можете указать общий интерфейс Dao как

public interface GenericDao <T, PK extends Serializable> {

/** Persist the newInstance object into database */
PK create(T newInstance);

/** Retrieve an object that was previously persisted to the database using
 *   the indicated id as primary key
 */
T read(PK id);

/** Save changes made to a persistent object.  */
void update(T transientObject);

/** Remove an object from persistent storage in the database */
void delete(T persistentObject);
}

и его реализация как

  public class GenericDaoHibernateImpl <T, PK extends Serializable>
implements GenericDao<T, PK>, FinderExecutor {
private Class<T> type;

public GenericDaoHibernateImpl(Class<T> type) {
    this.type = type;
}

public PK create(T o) {
    return (PK) getSession().save(o);
}

public T read(PK id) {
    return (T) getSession().get(type, id);
}

public void update(T o) {
    getSession().update(o);
}

public void delete(T o) {
    getSession().delete(o);
}

}

поэтому всякий раз, когда классы сервисов вызывает любой метод на любом Dao без какого-либо предположения о внутренней реализации метода

посмотрите ссылку GenericDao

Ответ 7

Спящий режим (либо как SessionManager, либо JPA EntityManager) - это DAO. Шаблон Repository - это, насколько я знаю, лучшее стартовое место. Существует большой image, на DDD Sample Website, который, как я думаю, говорит о том, как вы храните вещи отдельно.

В моем приложении есть интерфейсы, которые являются явными бизнес-действиями или значениями. Бизнес-правила находятся в модели домена, и такие вещи, как Hibernate, живут в инфраструктуре . Сервисы определяются на уровне домена как интерфейсы и реализованы в инфраструктуре в моем случае. Это означает, что для данного объекта домена Foo (совокупный корень в терминологии DDD) я обычно получаю Foo из FooService, а FooService - в FooRepository, что позволяет найти Foo на основе некоторых критериев. Эти критерии выражаются через параметры метода (возможно, сложные типы объектов), которые со стороны реализации, например, в HibernateFooRepository, будут переведены в критерий HQL или Hibernate.

Если вам нужна пакетная обработка, она должна существовать на уровне приложения и использовать службы домена для облегчения этого. StartBatchTransaction/EndBatchTransaction. Hibernate может прослушивать начальные/конечные события, чтобы координировать очистку, загрузку, что угодно.

Однако в конкретном случае сериализации сущностей домена я не вижу ничего плохого в принятии набора критериев и повторении по ним по одному (от корневых сущностей).

Я нахожу, что часто, в стремлении к разлуке, мы часто пытаемся сделать вещи полностью общими. Они не одни в одном - ваше приложение должно что-то делать, и что-то может и должно выражаться довольно явно.

Если вы можете заменить InMemoryFooRepository, где ранее использовался HibernateFooRepository, вы на правильном пути. Естественный поток через единицу и интеграцию, проверяя ваши объекты, поощряет это, когда вы придерживаетесь или, по крайней мере, стараетесь уважать слоирование, изложенное в изображении, которое я связал выше.

Ответ 8

У вас есть хорошие ответы здесь, я хотел бы добавить свои мысли об этом (кстати, это то, о чем нужно позаботиться и в нашем коде). Я также хотел бы остановиться на проблеме аннотаций Hibernate/JPA для объектов, которые могут потребоваться для использования вне вашего DAL (т.е. В бизнес-логике или даже отправлять на клиентскую сторону) -

A. Если вы используете шаблон GenericDAO для данного объекта, вы можете обнаружить, что ваш объект аннотируется с помощью Hibernate (или, возможно, JPA-аннотации), например @Таблица, @ManyToOne и т.д. -
это означает, что ваш клиентский код может содержать аннотации Hibernate/JPA, и для его компиляции потребуется соответствующая банка или какая-либо другая поддержка в вашем клиентском коде, например, если вы используйте GWT в качестве своего клиента (который может иметь поддержку аннотаций JPA для получения компиляции сущностей) и совместно использовать сущности между сервером и клиентским кодом, или если вы пишете клиент Java, который выполняет поиск bean, используя InitialContext против сервер приложений Java (в этом случае вам понадобится JAR

B. Еще один подход, который вы можете использовать, - это работа с аннотированным кодом Hibernate/JPA на стороне сервера и публикация веб-служб (скажем, веб-сервиса RESTFul или SOAP). Таким образом, клиент работает с "интерфейсом", который не раскрывает знания на Hibernate/JPA (например, WSDL в случае, если SOAP определяет контракт между клиентом службы и самой службой).
Разбирая архитектуру на сервис-ориентированную, вы получаете всевозможные преимущества, такие как свободное соединение, простоту замены фрагментов кода, и вы можете сосредоточить всю логику DAL в одной службе, которая обслуживает остальные ваши сервисы, и позже самостоятельно заменить DAL, если потребуется другой службой.

C. Вы можете использовать инфраструктуру отображения "объект для объекта", например dozer, чтобы сопоставить объекты классов с аннотациями Hibernate/JPA к тому, что я называть "истинные" POJO - т.е. - java beans без каких-либо аннотаций на них.

D. Наконец, в отношении аннотаций - зачем вообще использовать аннотации? Hibernate использует hbm xml файлы как альтернативу для "магии ORM" - таким образом ваши классы могут оставаться без аннотаций.

E. Один последний момент - я хотел бы предложить вам посмотреть на материал, который мы сделали в Ovirt - вы можете загрузить код git клонировать наше репо. Вы найдете там под движком /backend/manager/modules/bll - проект maven, содержащий нашу логику bll, и под движком /backend/manager/moduled/dal - наш слой DAL (хотя в настоящее время он реализован с помощью Spring -JDBC, и некоторые эксперименты с гибернацией, вы получите несколько хороших идей о том, как использовать их в своем коде. Я хотел бы добавить, что если вы пойдете на аналогичное решение, я предлагаю вам вводить DAO в свой код и не удерживать их в Singletone, как мы сделали с методами getXXXDao (это устаревший код, который мы должны стремиться удалить и перейти к инъекциям).

Ответ 9

Я бы порекомендовал вам позволить базе данных обрабатывать экспорт в CSV-операцию, а не строить ее самостоятельно на Java, но это не так эффективно. ORM не следует использовать для этих крупномасштабных пакетных операций, поскольку ORM следует использовать только для обработки транзакционных данных.

Крупномасштабные пакетные операции Java действительно должны выполняться JDBC напрямую с отключенной поддержкой транзакций.

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

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

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

Чтобы ответить на вопрос о ваших слоях. Хотя мне не нравится использование слоёв, потому что обычно это означает одно. Я предпочел бы использовать слово "компоненты", и у меня есть следующие группы компонентов.

приложения

  • - только аннотированные классы JPA, не логика постоянства, обычно простой JAR файл, но я рекомендую просто использовать его как пакет в EJB, а не иметь дело с проблемами пути класса.
  • контракты - WSDL и XSD файлы, которые определяют интерфейс между различными компонентами, - это веб-службы или просто пользовательский интерфейс.
  • сценарии транзакций - не имеющие аналогов состояния EJB, которые будут иметь в себе модули транзакций и персистентности, а также манипуляции и постоянство объектов домена. Они могут реализовывать интерфейсы, сгенерированные контрактами.
  • UI - отдельный проект WAR с вложенными в него EJB.

база данных

  • Диаграмма O/R - это контракт, согласованный командой приложений и данных, чтобы обеспечить МИНИМУМ, который предоставит база данных. Он не должен показывать все.
  • DDL - это реализация на стороне базы данных диаграммы O/R, которая будет содержать все, но, как правило, никто не должен заботиться, потому что это детали реализации.
  • пакетные операции, такие как экспорт или репликация
  • отчетность - предоставляет запросы для получения отчетов о стоимости бизнеса из системы.

наследие

  • контракты обмена сообщениями - это контракты, используемые системами обмена сообщениями, такими как JMS или WS-Уведомления или стандартные веб-службы.
  • их реализация
  • сценарии преобразования - используются для преобразования одного контракта в другой.

Ответ 10

Мне кажется, нам нужно еще раз взглянуть на слои. (Надеюсь, кто-то меня исправит, если я ошибаюсь.)

  • Front End/UI
  • Бизнес
  • Сервис /DAO

Итак, для случая создания отчета слои сломаются так.

  • Front End/UI
    • будет иметь пользовательский интерфейс с кнопкой "Получить отчет"
    • кнопка затем вызывается на уровне бизнес-уровня, который знает, что представляет отчет.
    • Данные, возвращаемые генератором отчетов, получают окончательное форматирование перед возвратом пользователю.
  • Бизнес
    • MyReportGenerator.GenerateReportData() или подобное будет называться
  • Сервис /DAO
    • внутри генератора отчетов DAO будут использоваться. DAOLocator.GetDAO(Entity.class); или аналогичные методы типа factory будут использоваться для получения DAO. возвращенные DAO расширят Общий интерфейс DAO

Ответ 11

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

Вы можете поместить свою бизнес-логику в уровень сервиса и связанные с базой данных на уровне Дао с помощью Hibernate.

Итак, если вам нужно что-то изменить в своей бизнес-логике, вы можете отредактировать свой уровень обслуживания, не изменяя DAO, и если вы хотите изменить уровень Dao, вы можете обойтись без изменения фактической бизнес-логики, то есть уровня обслуживания.