JPA-провайдер от Eclipse Foundation: кэш, weaving, настройка и отличия от Hibernate
Ответ: EclipseLink — open-source фреймворк персистентности от Eclipse Foundation. Он вырос из Oracle TopLink (код передан Eclipse в 2007 году) и является эталонной реализацией (reference implementation) спецификации JPA / Jakarta Persistence.
persistence.xml. Код на чистом JPA (EntityManager, @Entity, JPQL) работает с ним так же, как с Hibernate.
| Критерий | EclipseLink | Hibernate |
|---|---|---|
| Статус | Эталонная реализация JPA | Самая распространённая реализация, по умолчанию в Spring Boot |
| Кэш второго уровня | Общий (shared) кэш включён по умолчанию | Выключен, подключается отдельно |
| Ленивая загрузка to-one | Через weaving (изменение байткода) | Через прокси, создаваемые в runtime |
| Отслеживание изменений | Может отслеживать изменения атрибутов (через weaving) | Сравнение со снимком состояния (dirty checking) |
| Собственный язык запросов | JPQL + Expressions API | JPQL (HQL) + Criteria |
| Конфигурация | Свойства eclipselink.* |
Свойства hibernate.* |
| Экосистема | Jakarta EE, Oracle | Spring, широкая поддержка инструментов |
Ответ: Нужны зависимость EclipseLink и указание провайдера в persistence unit.
Maven-зависимость: org.eclipse.persistence:org.eclipse.persistence.jpa
<persistence-unit name="my-unit" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.example.User</class>
<properties>
<property name="jakarta.persistence.jdbc.url"
value="jdbc:postgresql://localhost:5432/mydb"/>
<property name="jakarta.persistence.jdbc.user" value="app"/>
<property name="jakarta.persistence.jdbc.password" value="secret"/>
<property name="eclipselink.logging.level" value="INFO"/>
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
</properties>
</persistence-unit>
eclipselink.logging.level.sql=FINE + eclipselink.logging.parameters=true — вывод SQL с параметрамиjakarta.persistence.schema-generation.database.action (create, drop-and-create) — генерация схемы по стандарту; у EclipseLink также есть eclipselink.ddl-generationeclipselink.target-database — явно задать диалект СУБДeclipselink.weaving — включить, выключить или перейти на статический weavingjakarta.persistence, а не javax.persistence.
Ответ: Weaving — это изменение байткода классов сущностей (динамически при загрузке или статически при сборке), чтобы добавить в них служебную логику EclipseLink. В отличие от Hibernate, EclipseLink не создаёт для ленивых to-one связей прокси-подклассы.
@OneToOne и @ManyToOne и для базовых атрибутов (@Basic(fetch = LAZY))-javaagent:eclipselink.jarОтвет: В EclipseLink есть кэш второго уровня (shared cache), и он включён по умолчанию для сущностей. Объекты, прочитанные одним EntityManager, кладутся в общий кэш EntityManagerFactory и используются другими без обращения к БД.
@Entity
@Cache(
type = CacheType.SOFT, // тип хранения
size = 1000, // размер кэша
expiry = 600000, // время жизни, мс
coordinationType = CacheCoordinationType.INVALIDATE_CHANGED_OBJECTS
)
public class Product { ... }
@Entity
@Cacheable(false) // отключить кэш для этой сущности
public class Account { ... }
jakarta.persistence.sharedCache.mode — ALL, NONE, ENABLE_SELECTIVE, DISABLE_SELECTIVEjakarta.persistence.cache.retrieveMode = BYPASS (не читать из кэша) и eclipselink.refresh = true (обновить из БД)emf.getCache().evictAll()expiry, отключить кэш для таких сущностей или использовать @Cache(alwaysRefresh = true).
Ответ: Стандартные способы JPA работают: JOIN FETCH в JPQL и entity graph. Дополнительно у EclipseLink есть собственные подсказки и аннотации для пакетной загрузки.
// стандартный JPQL
em.createQuery(
"SELECT o FROM Order o JOIN FETCH o.items", Order.class)
.getResultList();
// подсказка EclipseLink: загрузить items отдельным запросом для всех заказов сразу
em.createQuery("SELECT o FROM Order o", Order.class)
.setHint("eclipselink.batch", "o.items")
.setHint("eclipselink.batch.type", "IN") // JOIN, EXISTS или IN
.getResultList();
// на уровне маппинга
@OneToMany(mappedBy = "order")
@BatchFetch(BatchFetchType.IN)
private List<OrderItem> items;
eclipselink.join-fetch и @JoinFetch — загрузка через JOINeclipselink.batch и @BatchFetch — пакетная загрузка вторым запросомbatch часто лучше JOIN FETCH для нескольких коллекций: он не умножает строки результата.
Ответ: Помимо стандартного JPA, EclipseLink предоставляет расширения в пакете org.eclipse.persistence.annotations:
| Возможность | Для чего |
|---|---|
@Cache |
Тонкая настройка кэша сущности |
@Convert, @ObjectTypeConverter |
Конвертация значений между полем и колонкой |
@AdditionalCriteria |
Дополнительное условие, автоматически добавляемое ко всем запросам (например, фильтр «мягкого удаления») |
@Multitenant |
Мультиарендность: разделение данных арендаторов в одной схеме |
@HistoryPolicy |
Хранение истории изменений строк |
@BatchFetch, @JoinFetch |
Стратегии загрузки связей |
@Customizer |
Программная настройка дескрипторов сущности |
| Хранимые процедуры, PL/SQL, иерархические запросы Oracle | Расширенная работа с Oracle Database |
@Entity
@AdditionalCriteria("this.deleted = false")
public class Document {
@Id Long id;
boolean deleted;
}
// em.createQuery("SELECT d FROM Document d") автоматически добавит условие deleted = false
Ответ: Внутри JPA-слой EclipseLink построен поверх собственного ядра, унаследованного от TopLink.
| Понятие JPA | Внутренний аналог EclipseLink |
|---|---|
EntityManagerFactory |
ServerSession — общая сессия с пулом соединений и общим кэшем |
EntityManager |
Обёртка над клиентской сессией и UnitOfWork |
| Контекст персистентности | UnitOfWork — копирует объекты из общего кэша в рабочую копию и при commit записывает изменения |
Из EntityManager можно получить родные объекты:
JpaEntityManager jpaEm = em.unwrap(JpaEntityManager.class);
UnitOfWork uow = jpaEm.getUnitOfWork();
Session session = jpaEm.getServerSession();
Ответ: Spring поддерживает EclipseLink через EclipseLinkJpaVendorAdapter, но Spring Boot по умолчанию настроен на Hibernate, поэтому автоконфигурацию нужно заменить вручную.
@Configuration
public class JpaConfig {
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource ds) {
var em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds);
em.setPackagesToScan("com.example.domain");
em.setJpaVendorAdapter(new EclipseLinkJpaVendorAdapter());
Map<String, Object> props = new HashMap<>();
props.put("eclipselink.weaving", "false"); // или настроить weaving
props.put("eclipselink.logging.level.sql", "FINE");
em.setJpaPropertyMap(props);
return em;
}
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
spring-boot-starter-data-jpaeclipselink.weaving=false) и принять eager для to-one, либо подключить агент или статический weavingspring.jpa.hibernate.*) работает только с HibernateОтвет:
FUNCTION('...') для вызова функций БД)createNativeQueryExpressionBuilder@NamedQuery, @NamedNativeQuery@NamedStoredProcedureQuery
// Expressions API
ExpressionBuilder b = new ExpressionBuilder();
Expression e = b.get("name").equal("Ann").and(b.get("age").greaterThan(18));
ReadAllQuery query = new ReadAllQuery(User.class, e);
List<User> users = (List<User>) uow.executeQuery(query);
eclipselink.cache-usage — как использовать кэшeclipselink.refresh — перечитать из БДeclipselink.query-results-cache — кэш результатов запросаeclipselink.read-only — объекты не отслеживаются на измененияОтвет:
| Проблема | Причина и решение |
|---|---|
| Устаревшие данные при чтении | Shared cache: данные изменили вне приложения. Использовать expiry, @Cacheable(false) или eclipselink.refresh |
| Ленивая загрузка to-one не работает | Weaving не включён. Настроить агент, статический weaving или смириться с eager |
| Ошибка запуска из-за классов сущностей | Сущность не указана в persistence.xml в Java SE: добавить <class> или <exclude-unlisted-classes>false</exclude-unlisted-classes> |
| Лишние запросы (N+1) | Использовать JOIN FETCH, eclipselink.batch, @BatchFetch |
| Разное поведение после перехода с Hibernate | Различия в кэше, flush, weaving и значениях по умолчанию. Проверять тестами на реальной БД |
| Нет SQL в логах | Включить eclipselink.logging.level.sql=FINE |
@Cache, @Formula, свойства eclipselink.*/hibernate.*) быстро привязывают проект к выбранной реализации.