Рубрики
Без рубрики

Как всегда, полный исходный код доступен || на Github || .

Как всегда, полный исходный код доступен || на Github || .

Автор оригинала: baeldung.

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();
      Stream list = 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 .