1. Обзор
Reladomo (ранее известный как Mithra) -это фреймворк объектно-реляционного отображения (ORM) для Java , разработчик в Goldman Sachs , в настоящее время выпущенный как проект с открытым исходным кодом. Фреймворк предоставляет функции, обычно необходимые для ORM, а также некоторые дополнительные функции.
Давайте рассмотрим некоторые ключевые особенности Rella domo :
- он может генерировать классы Java, а также сценарии DDL
- он управляется метаданными, записанными в XML-файлах
- сгенерированный код является расширяемым
- язык запросов является объектно-ориентированным и строго типизированным
- фреймворк обеспечивает поддержку сегментирования (одна и та же схема, разные наборы данных)
- поддержка тестирования также включена
- он предоставляет полезные функции, такие как кэширование производительности и транзакции
В следующих разделах мы рассмотрим настройку и некоторые основные примеры использования.
2. Настройка Maven
Чтобы начать использовать ФОРМУ, нам нужно добавить зависимость reladomo |/в ваш pom.xml файл:
com.goldmansachs.reladomo reladomo 16.5.1
Мы будем использовать базу данных H2 для наших примеров, поэтому давайте также добавим зависимость h2 :
com.h2database h2 1.4.196
В дополнение к этому нам нужно настроить плагины, которые будут генерировать классы и файлы SQL и загружать их во время выполнения.
Для генерации файлов мы можем использовать задачи, которые выполняются с помощью maven-antrun-плагина . Во-первых, давайте посмотрим, как мы можем определить задачу для создания классов Java:
maven-antrun-plugin generateMithra generate-sources run
Задача gen-reladomo использует предоставленный генератор Mithra для создания файлов Java на основе конфигурации в ReladomoClassList.xml файл. Мы подробнее рассмотрим, что содержит этот файл, в следующем разделе.
Задачи также имеют два свойства, которые определяют расположение сгенерированных файлов:
- generatedDir – содержит классы, которые не должны быть изменены или версированы
- nonGeneratedDir – сгенерированные конкретные классы объектов, которые могут быть дополнительно настроены и версированы
Таблицы базы данных, соответствующие объектам Java, могут быть созданы вручную или автоматически с помощью сценариев DDL, сгенерированных второй задачей Ant :
Эта задача использует MithraDbDefinitionGenerator на основе того же ReladomoClassList.xml файл, упомянутый ранее. Сценарии SQL будут помещены в каталог generated-db/sql .
Чтобы завершить определение этого плагина, мы также должны добавить две зависимости, используемые для создания:
maven-antrun-plugin //... com.goldmansachs.reladomo reladomogen 16.5.1 com.goldmansachs.reladomo reladomo-gen-util 16.5.1
Наконец, используя build-helper-maven-plugin , мы можем добавить сгенерированные файлы в путь к классу:
org.codehaus.mojo build-helper-maven-plugin add-source generate-sources add-source ${project.build.directory}/generated-sources/reladomo add-resource generate-resources add-resource ${project.build.directory}/generated-db/
Добавление сценариев DDL необязательно. В нашем примере мы будем использовать базу данных в памяти, поэтому мы хотим выполнить сценарии для создания таблиц.
3. Конфигурация XML
Метаданные для фреймворка Reladomo могут быть определены в нескольких XML-файлах.
3.1. Объектные XML-файлы
Каждая сущность, которую мы хотим создать, должна быть определена в своем XML-файле.
Давайте создадим простой пример с двумя сущностями: отделами и сотрудниками. Вот визуальное представление нашей модели предметной области:
Давайте определим первый Department.xml файл:
com.baeldung.reladomo Department departments Employee.departmentId = this.id
Мы можем видеть выше сущность определена внутри корневого элемента, называемого Объектом Митры . Затем мы указали пакет, класс и имя соответствующей таблицы базы данных.
Каждое свойство типа определяется с помощью элемента Attribute , для которого мы можем указать имя, тип Java и имя столбца.
Мы можем описать отношения между объектами с помощью тега Relationship . В нашем примере мы определили отношение один ко многим между объектами Отдел и Сотрудник на основе выражения:
Employee.departmentId = this.id
То имя обратной связи атрибут можно использовать для того, чтобы сделать связь двунаправленной, не определяя ее дважды.
Атрибут related Is Dependent позволяет нам каскадировать операции.
Далее, давайте создадим Employee.xml файл аналогично:
com.baeldung.reladomo Employee employees
3.2. ReladomoClassList.xml Файл
Rella domo нужно рассказать об объектах, которые он должен генерировать.
В разделе Maven мы определили ReladomoClassList.xml файл в качестве источника для задач генерации, поэтому пришло время создать файл:
Это простой файл, содержащий список сущностей, для которых будут созданы классы на основе конфигурации XML.
4. Сгенерированные классы
Теперь у нас есть все элементы, необходимые для начала генерации кода, создав приложение Maven с помощью команды mvn clean install .
Конкретные классы будут сгенерированы в папке src/main/java в указанном пакете:
Это простые классы, в которые мы можем добавить наш пользовательский код. Например, класс Department содержит только конструктор, который не следует удалять:
public class Department extends DepartmentAbstract { public Department() { super(); // You must not modify this constructor. Mithra calls this internally. // You can call this constructor. You can also add new constructors. } }
Если мы хотим добавить пользовательский конструктор в этот класс, он также должен вызвать родительский конструктор:
public Department(long id, String name){ super(); this.setId(id); this.setName(name); }
Эти классы основаны на абстрактных и служебных классах в папке generated-sources/rella domo :
Основными типами классов в этой папке являются:
- DepartmentAbstract и EmployeeAbstract классы – который содержит методы для работы с определенными сущностями
- DepartmentListAbstract и EmployeeListAbstract – содержит методы работы со списками отделов и сотрудников
- DepartmentFinder и EmployeeFinder – они предоставляют методы для запроса сущностей
- другие классы утилит
Генерируя эти классы, большая часть кода, необходимого для выполнения операций CRUD над нашими сущностями, уже создана для нас.
5. Приложение Reladomo
Для выполнения операций с базой данных нам нужен класс диспетчера соединений, который позволяет нам получать соединения с базой данных.
5.1. Диспетчер соединений
При работе с одной базой данных мы можем реализовать интерфейс Source less Connection Manager :
public class ReladomoConnectionManager implements SourcelessConnectionManager { private static ReladomoConnectionManager instance; private XAConnectionManager xaConnectionManager; public static synchronized ReladomoConnectionManager getInstance() { if (instance == null) { instance = new ReladomoConnectionManager(); } return instance; } private ReladomoConnectionManager() { this.createConnectionManager(); } //... }
Наш класс Rella domo Connection Manager реализует одноэлементный шаблон и основан на XA Connection Manager , который является служебным классом для менеджера транзакционных соединений.
Давайте подробнее рассмотрим метод createConnectionManager() :
private XAConnectionManager createConnectionManager() { xaConnectionManager = new XAConnectionManager(); xaConnectionManager.setDriverClassName("org.h2.Driver"); xaConnectionManager.setJdbcConnectionString("jdbc:h2:mem:myDb"); xaConnectionManager.setJdbcUser("sa"); xaConnectionManager.setJdbcPassword(""); xaConnectionManager.setPoolName("My Connection Pool"); xaConnectionManager.setInitialSize(1); xaConnectionManager.setPoolSize(10); xaConnectionManager.initialisePool(); return xaConnectionManager; }
В этом методе мы задали свойства, необходимые для создания соединения с базой данных H2 в памяти.
Кроме того, нам нужно реализовать несколько методов из интерфейса Source less Connection Manager :
@Override public Connection getConnection() { return xaConnectionManager.getConnection(); } @Override public DatabaseType getDatabaseType() { return H2DatabaseType.getInstance(); } @Override public TimeZone getDatabaseTimeZone() { return TimeZone.getDefault(); } @Override public String getDatabaseIdentifier() { return "myDb"; } @Override public BulkLoader createBulkLoader() throws BulkLoaderException { return null; }
Наконец, давайте добавим пользовательский метод для выполнения сгенерированных сценариев DDL, которые создают наши таблицы базы данных:
public void createTables() throws Exception { Path ddlPath = Paths.get(ClassLoader.getSystemResource("sql").toURI()); try ( Connection conn = xaConnectionManager.getConnection(); Streamlist = Files.list(ddlPath)) { list.forEach(path -> { try { RunScript.execute(conn, Files.newBufferedReader(path)); } catch (SQLException | IOException exc){ exc.printStackTrace(); } }); } }
Это, конечно, не обязательно для производственного приложения, где ваши таблицы не будут воссоздаваться для каждого выполнения.
5.2. Инициализация Reladomo
Процесс инициализации Reladomo использует файл конфигурации, который определяет класс диспетчера соединений и используемые типы объектов. Давайте определим/| ReladomoRuntimeConfig.xml файл:
Затем мы можем создать основной класс, в котором сначала вызовем метод createtable () , а затем используем класс Mithra Manager для загрузки конфигурации и инициализации Reladomo :
public class ReladomoApplication { public static void main(String[] args) { try { ReladomoConnectionManager.getInstance().createTables(); } catch (Exception e1) { e1.printStackTrace(); } MithraManager mithraManager = MithraManagerProvider.getMithraManager(); mithraManager.setTransactionTimeout(120); try (InputStream is = ReladomoApplication.class.getClassLoader() .getResourceAsStream("ReladomoRuntimeConfig.xml")) { MithraManagerProvider.getMithraManager() .readConfiguration(is); //execute operations } catch (IOException exc){ exc.printStackTrace(); } } }
5.3. Выполнение операций CRUD
Теперь давайте использовать Reladomo -сгенерированные классы для выполнения нескольких операций над нашими сущностями.
Сначала давайте создадим два объекта Department и Employee , а затем сохраним оба с помощью метода cascade Insert() :
Department department = new Department(1, "IT"); Employee employee = new Employee(1, "John"); department.getEmployees().add(employee); department.cascadeInsert();
Каждый объект также можно сохранить отдельно, вызвав метод insert () . В нашем примере можно использовать cascade Insert () , потому что мы добавили атрибут relatedIsDependent=true в наше определение отношений.
Для запроса объектов мы можем использовать сгенерированные Искатель занятия:
Department depFound = DepartmentFinder .findByPrimaryKey(1); Employee empFound = EmployeeFinder .findOne(EmployeeFinder.name().eq("John"));
Объекты, полученные таким образом, являются “живыми” объектами, то есть любое изменение в них с помощью сеттеров немедленно отражается в базе данных:
empFound.setName("Steven");
Чтобы избежать такого поведения, мы можем получить отдельные объекты:
Department depDetached = DepartmentFinder .findByPrimaryKey(1).getDetachedCopy();
Для удаления объектов мы можем использовать метод delete() :
empFound.delete();
5.4. Управление Транзакциями
Если мы хотим, чтобы набор операций выполнялся или не выполнялся как единое целое, мы можем обернуть их в транзакцию:
mithraManager.executeTransactionalCommand(tx -> { Department dep = new Department(2, "HR"); Employee emp = new Employee(2, "Jim"); dep.getEmployees().add(emp); dep.cascadeInsert(); return null; });
6. Поддержка тестирования Reladomo
В приведенных выше разделах мы написали наши примеры в основном классе Java.
Если мы хотим написать тесты для нашего приложения, один из способов сделать это-просто написать тот же код в тестовом классе.
Однако для лучшей поддержки тестов Reladomo также предоставляет класс MithraTestResource . Это позволяет нам использовать другую конфигурацию и базу данных в памяти только для тестов.
Во-первых, нам нужно добавить дополнительную зависимость reladomo-test-util вместе с зависимостью junit :
com.goldmansachs.reladomo reladomo-test-util 16.5.1 junit junit 4.12
Далее, мы должны создать ReladomoTestConfig.xml файл, который использует Диспетчер соединений для тестов класс:
Этот диспетчер соединений настраивает базу данных в памяти H2 , используемую только для тестов.
Удобной особенностью класса Mithra Test Resource является то, что мы можем предоставлять текстовые файлы с тестовыми данными в следующем формате:
class com.baeldung.reladomo.Department id, name 1, "Marketing" class com.baeldung.reladomo.Employee id, name 1, "Paul"
Давайте создадим JUnit тестовый класс и настроим наш MithraTestResource экземпляр в @Перед методом:
public class ReladomoTest { private MithraTestResource mithraTestResource; @Before public void setUp() throws Exception { this.mithraTestResource = new MithraTestResource("reladomo/ReladomoTestConfig.xml"); ConnectionManagerForTests connectionManager = ConnectionManagerForTests.getInstanceForDbName("testDb"); this.mithraTestResource.createSingleDatabase(connectionManager); mithraTestResource.addTestDataToDatabase("reladomo/test-data.txt", connectionManager); this.mithraTestResource.setUp(); } }
Затем мы можем написать простой метод @Test , который проверяет, были ли загружены наши тестовые данные:
@Test public void whenGetTestData_thenOk() { Employee employee = EmployeeFinder.findByPrimaryKey(1); assertEquals(employee.getName(), "Paul"); }
После выполнения тестов необходимо очистить тестовую базу данных:
@After public void tearDown() throws Exception { this.mithraTestResource.tearDown(); }
7. Заключение
В этой статье мы рассмотрели основные функции Rella domo ORM framework, а также настройки и примеры общего использования.
Исходный код примеров можно найти на GitHub .