Может так сложиться, что в проекте будет некоторое количество классов предметной области с одинаковыми именами, но расположенные в разных пакетах. Например, два класса Client. Им могут быть сопоставлены разные таблицы, но в JPA запросах вида «select c from Client c» будет иметь место неоднозначность. Чтобы эту неоднозначность снять, можно воспользоваться параметром name аннотации @Entity.
В пакете external создадим следующий класс предметной области:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.demo.app.external; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; @Entity(name = "ExternalClient") @Table(name = "EXTERNAL_CLIENT") public class Client { @Id @GeneratedValue Long id; private String fullName; //Конструкторы, геттеры и сеттеры, equals(), hashCode() и т.д. } |
В пакете internal создадим парный ему класс с таким же названием:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.demo.app.internal; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; @Entity(name = "InternalClient") @Table(name = "INTERNAL_CLIENT") public class Client { @Id @GeneratedValue Long id; private String innerNumber; //Конструкторы, геттеры и сеттеры, equals(), hashCode() и т.д. } |
Обратим внимание на то, что параметре аннотации @Entity в одном случае мы указали ExternalClient, а во втором — InternalClient. Теперь в JPA запросах мы можем различать эти две сущности с помощью заданных имён.
Проверим, что это действительно так.
Создадим файл package-info.java следующего содержания:
1 2 3 4 5 6 7 |
@org.hibernate.annotations.NamedQueries({ @org.hibernate.annotations.NamedQuery( name = "findAllInternalClients", query = "select i from InternalClient i" ) }) package com.demo.app; |
Такой подход позволяет создавать именованные запросы на уровне пакета. Здесь для простоты мы создадим только один именованный запрос, который будет обращаться к сущности com.demo.app.internal.Client
, поскольку в самом JPA запросе мы ссылаемся на InternalClient, а именно у com.demo.app.internal.Client
стоит аннотация @Entity(name = "InternalClient")
.
Создадим бин репозитория:
1 2 3 |
@Repository public interface InternalClientRepository extends JpaRepository<com.demo.app.internal.Client, Long> { } |
Создадим тест, который докажет, что именованный запрос работает так, как ожидается:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@SpringBootTest class TestApplicationTests { @Autowired InternalClientRepository internalClientRepository; @PersistenceContext private EntityManager entityManager; @BeforeEach void cleanTable() { internalClientRepository.deleteAll(); } @Test void checkEntityNames() { var client = new com.demo.app.internal.Client(); internalClientRepository.save(client); Query findOlderQuery = entityManager.createNamedQuery("findAllInternalClients"); List<Client> fetchResult = findOlderQuery.getResultList(); assertEquals(fetchResult.size(), 1); assertEquals(fetchResult.get(0), client); } } |
Если тест вдруг провалится, то убедитесь, что вы правильно переопределили методы equals() и hashCode().
В тесте мы создаём объект типа com.demo.app.internal.Client
и сохраняем его в базу. Затем делаем выборку с помощью именованного запроса «select i from InternalClient i». И действительно, запрос находит то, что мы и ожидаем. Никакой путаницы между классами internal.Client и external.Client не возникает, несмотря на то, что у них совпадают имена.