EclipseLink
EclipseLinkは「Java Persistence API(JPA)の公式参照実装」として開発された、エンタープライズレベルのオブジェクト関係マッピング(ORM)フレームワークです。JPA 2.0仕様の完全実装に加え、高度なキャッシュ機能、バッチ処理、多様なデータベースサポートを提供。Oracle Corporationによる強力なサポートのもと、大規模企業システムでの安定稼働実績を持ち、Java企業開発におけるデータ永続化ソリューションの標準的選択肢として確固たる地位を築いています。
GitHub概要
eclipse-ee4j/eclipselink
Eclipselink project
トピックス
スター履歴
ライブラリ
EclipseLink
概要
EclipseLinkは「Java Persistence API(JPA)の公式参照実装」として開発された、エンタープライズレベルのオブジェクト関係マッピング(ORM)フレームワークです。JPA 2.0仕様の完全実装に加え、高度なキャッシュ機能、バッチ処理、多様なデータベースサポートを提供。Oracle Corporationによる強力なサポートのもと、大規模企業システムでの安定稼働実績を持ち、Java企業開発におけるデータ永続化ソリューションの標準的選択肢として確固たる地位を築いています。
詳細
EclipseLink 2025年版は、15年以上の開発実績により成熟したJPA実装とエンタープライズ級の機能を提供し続けています。JPA標準仕様の完全サポートに加え、高度なキャッシュ機能、クエリ最適化、接続プール管理など企業システムに必要な機能を網羅。Oracle Database、MySQL、PostgreSQLなど主要データベースとの最適化された統合により、高いパフォーマンスを実現。Spring BootやJakarta EEなどの主要フレームワークとのシームレスな統合により、モダンなJava開発環境での生産性向上を支援します。
主な特徴
- JPA標準準拠: JPA 2.0仕様の完全な参照実装
- 高度なキャッシュシステム: 複数レベルキャッシュによる大幅なパフォーマンス向上
- 企業級機能: バッチ処理、接続プール、楽観的ロック対応
- データベース最適化: Oracle、MySQL、PostgreSQL等主要DBとの統合最適化
- 拡張クエリ機能: JPA標準を超えた高度なクエリヒントとカスタマイズ
- Spring統合: Spring Boot/Spring Data JPAとの完全統合サポート
メリット・デメリット
メリット
- Java Persistence APIの公式参照実装による高い信頼性と標準準拠
- Oracle Corporationによる強力な商用サポートと長期保守体制
- 高度なキャッシュ機能による大幅なパフォーマンス向上
- 企業システムに必要なバッチ処理、接続プール等の包括的機能
- Oracle Database等との最適化された統合による優れたパフォーマンス
- Spring Boot等モダンフレームワークとの優れた統合性
デメリット
- Hibernateと比較して学習リソースやコミュニティサポートが限定的
- 設定項目が多く初期設定が複雑になりがち
- Oracle製品への依存度が高く、他社製品との統合で制約が生じる場合
- 高度な機能を活用するにはJPA仕様を超えた専用知識が必要
- 小規模プロジェクトには機能が過剰でオーバーエンジニアリングになりがち
- Hibernateと比較してサードパーティツールやプラグインが少ない
参考ページ
書き方の例
基本セットアップ
<!-- Maven dependency -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
<version>4.0.3</version>
</dependency>
<!-- persistence.xml 設定 -->
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="usersPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.example.model.User</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="password"/>
<!-- EclipseLink 固有設定 -->
<property name="eclipselink.ddl-generation" value="create-or-extend-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="database"/>
<property name="eclipselink.logging.level" value="INFO"/>
<property name="eclipselink.cache.shared.default" value="true"/>
</properties>
</persistence-unit>
</persistence>
モデル定義と基本操作
// エンティティクラス定義
@Entity
@Table(name = "users")
@Cache(type = CacheType.FULL, size = 1000) // EclipseLink キャッシュ設定
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "email")
private String email;
@Version // 楽観的ロック
private Long version;
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
// getters, setters, constructors
public User() {}
public User(String username, String email) {
this.username = username;
this.email = email;
}
// アクセサメソッド
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Long getVersion() { return version; }
public void setVersion(Long version) { this.version = version; }
}
// 基本的なCRUD操作
public class UserService {
private EntityManagerFactory emf;
private EntityManager em;
public UserService() {
emf = Persistence.createEntityManagerFactory("usersPU");
em = emf.createEntityManager();
}
// ユーザー作成
public User createUser(String username, String email) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
User user = new User(username, email);
em.persist(user);
tx.commit();
return user;
} catch (Exception e) {
if (tx.isActive()) tx.rollback();
throw e;
}
}
// ユーザー取得(ID)
public User findUserById(Long id) {
return em.find(User.class, id);
}
// ユーザー更新
public User updateUser(Long id, String newEmail) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
User user = em.find(User.class, id);
if (user != null) {
user.setEmail(newEmail);
user = em.merge(user);
}
tx.commit();
return user;
} catch (Exception e) {
if (tx.isActive()) tx.rollback();
throw e;
}
}
// ユーザー削除
public boolean deleteUser(Long id) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
User user = em.find(User.class, id);
if (user != null) {
em.remove(user);
tx.commit();
return true;
}
tx.rollback();
return false;
} catch (Exception e) {
if (tx.isActive()) tx.rollback();
throw e;
}
}
public void close() {
if (em != null) em.close();
if (emf != null) emf.close();
}
}
高度なクエリ操作
public class UserQueryService {
private EntityManager em;
public UserQueryService(EntityManager em) {
this.em = em;
}
// JPQL クエリ
public List<User> findUsersByUsername(String username) {
return em.createQuery(
"SELECT u FROM User u WHERE u.username LIKE :username",
User.class)
.setParameter("username", "%" + username + "%")
.getResultList();
}
// ネイティブSQL クエリ
public List<User> findActiveUsers() {
return em.createNativeQuery(
"SELECT * FROM users WHERE created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)",
User.class)
.getResultList();
}
// EclipseLink クエリヒント使用
public List<User> findUsersWithCaching() {
return em.createQuery("SELECT u FROM User u", User.class)
.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheThenDatabase)
.setHint(QueryHints.QUERY_RESULTS_CACHE, HintValues.TRUE)
.setHint(QueryHints.QUERY_RESULTS_CACHE_SIZE, 100)
.getResultList();
}
// バッチフェッチ
public List<User> findUsersWithBatchFetch() {
return em.createQuery("SELECT u FROM User u", User.class)
.setHint(QueryHints.BATCH_FETCH_TYPE, BatchFetchType.IN)
.setHint(QueryHints.BATCH_SIZE, 25)
.getResultList();
}
// 動的クエリ(Criteria API)
public List<User> findUsersByCriteria(String username, String email) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (username != null && !username.isEmpty()) {
predicates.add(cb.like(user.get("username"), "%" + username + "%"));
}
if (email != null && !email.isEmpty()) {
predicates.add(cb.like(user.get("email"), "%" + email + "%"));
}
query.where(predicates.toArray(new Predicate[0]));
query.orderBy(cb.asc(user.get("username")));
return em.createQuery(query).getResultList();
}
// ページネーション
public List<User> findUsersWithPagination(int page, int size) {
return em.createQuery("SELECT u FROM User u ORDER BY u.id", User.class)
.setFirstResult(page * size)
.setMaxResults(size)
.getResultList();
}
}
リレーション操作
// Order エンティティ(多対一リレーション)
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "order_number")
private String orderNumber;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrderItem> items = new ArrayList<>();
@Column(name = "total_amount")
private BigDecimal totalAmount;
// constructors, getters, setters
public Order() {}
public Order(String orderNumber, User user) {
this.orderNumber = orderNumber;
this.user = user;
}
// ヘルパーメソッド
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
}
// OrderItem エンティティ
@Entity
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
@Column(name = "product_name")
private String productName;
@Column(name = "quantity")
private Integer quantity;
@Column(name = "price")
private BigDecimal price;
// constructors, getters, setters
}
// リレーション操作サービス
public class OrderService {
private EntityManager em;
public OrderService(EntityManager em) {
this.em = em;
}
// 注文作成(リレーション含む)
public Order createOrderWithItems(Long userId, String orderNumber,
List<OrderItemData> itemsData) {
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
// ユーザー取得
User user = em.find(User.class, userId);
if (user == null) {
throw new EntityNotFoundException("User not found: " + userId);
}
// 注文作成
Order order = new Order(orderNumber, user);
// 注文アイテム追加
BigDecimal total = BigDecimal.ZERO;
for (OrderItemData itemData : itemsData) {
OrderItem item = new OrderItem();
item.setProductName(itemData.getProductName());
item.setQuantity(itemData.getQuantity());
item.setPrice(itemData.getPrice());
order.addItem(item);
total = total.add(itemData.getPrice().multiply(
BigDecimal.valueOf(itemData.getQuantity())));
}
order.setTotalAmount(total);
em.persist(order);
tx.commit();
return order;
} catch (Exception e) {
if (tx.isActive()) tx.rollback();
throw e;
}
}
// リレーション込みでの注文取得
public Order findOrderWithItems(Long orderId) {
return em.createQuery(
"SELECT o FROM Order o " +
"LEFT JOIN FETCH o.items " +
"LEFT JOIN FETCH o.user " +
"WHERE o.id = :orderId", Order.class)
.setParameter("orderId", orderId)
.getSingleResult();
}
// ユーザーの全注文取得
public List<Order> findOrdersByUser(Long userId) {
return em.createQuery(
"SELECT o FROM Order o " +
"WHERE o.user.id = :userId " +
"ORDER BY o.id DESC", Order.class)
.setParameter("userId", userId)
.getResultList();
}
}
実用例
// Spring Boot統合例
@Configuration
@EnableJpaRepositories
public class JpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.example.model");
// EclipseLink プロバイダー設定
EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
em.setJpaVendorAdapter(vendorAdapter);
// EclipseLink固有プロパティ
Properties properties = new Properties();
properties.setProperty("eclipselink.cache.shared.default", "true");
properties.setProperty("eclipselink.cache.size.default", "1000");
properties.setProperty("eclipselink.query-results-cache", "true");
properties.setProperty("eclipselink.logging.level", "INFO");
em.setJpaProperties(properties);
return em;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb?useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
}
// リポジトリ実装(Spring Data JPA風)
@Repository
@Transactional
public class UserRepository {
@PersistenceContext
private EntityManager em;
public User save(User user) {
if (user.getId() == null) {
em.persist(user);
return user;
} else {
return em.merge(user);
}
}
public Optional<User> findById(Long id) {
User user = em.find(User.class, id);
return Optional.ofNullable(user);
}
public List<User> findAll() {
return em.createQuery("SELECT u FROM User u", User.class)
.getResultList();
}
public Optional<User> findByUsername(String username) {
try {
User user = em.createQuery(
"SELECT u FROM User u WHERE u.username = :username", User.class)
.setParameter("username", username)
.getSingleResult();
return Optional.of(user);
} catch (NoResultException e) {
return Optional.empty();
}
}
public void deleteById(Long id) {
User user = em.find(User.class, id);
if (user != null) {
em.remove(user);
}
}
// EclipseLink固有機能を活用したメソッド
public List<User> findAllWithCache() {
return em.createQuery("SELECT u FROM User u", User.class)
.setHint(QueryHints.CACHE_USAGE, CacheUsage.CheckCacheThenDatabase)
.getResultList();
}
}
// サービス層(実用的なビジネスロジック)
@Service
@Transactional
public class UserBusinessService {
private final UserRepository userRepository;
public UserBusinessService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User registerUser(String username, String email) {
// 重複チェック
if (userRepository.findByUsername(username).isPresent()) {
throw new IllegalArgumentException("Username already exists: " + username);
}
User user = new User(username, email);
return userRepository.save(user);
}
public User updateUserEmail(Long userId, String newEmail) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("User not found: " + userId));
user.setEmail(newEmail);
return userRepository.save(user);
}
@Transactional(readOnly = true)
public List<User> getAllUsersWithCaching() {
return userRepository.findAllWithCache();
}
}