Hibernate ORM
HibernateはJava界のデファクトスタンダードORMライブラリです。O/Rマッパー(Object-Relational Mapping)として、オブジェクト指向のJavaオブジェクトとリレーショナルデータベースを巧妙に関連付けるツールです。Jakarta EE(旧Java EE)の一部として標準化され、HQL(Hibernate Query Language)やCriteria APIによる柔軟なデータアクセスを提供します。
GitHub概要
hibernate/hibernate-orm
Hibernate's core Object/Relational Mapping functionality
スター6,188
ウォッチ303
フォーク3,649
作成日:2010年10月4日
言語:Java
ライセンス:Apache License 2.0
トピックス
databaseenvershibernatehibernate-ormjakarta-persistencejakartaeejavajdbcjpaobject-relational-mapperobject-relational-mappingormpersistencepersistence-frameworksql
スター履歴
データ取得日時: 2025/7/17 02:30
ライブラリ
Hibernate ORM
概要
HibernateはJava界のデファクトスタンダードORMライブラリです。O/Rマッパー(Object-Relational Mapping)として、オブジェクト指向のJavaオブジェクトとリレーショナルデータベースを巧妙に関連付けるツールです。Jakarta EE(旧Java EE)の一部として標準化され、HQL(Hibernate Query Language)やCriteria APIによる柔軟なデータアクセスを提供します。
詳細
Hibernate ORM 7.0は2025年5月にリリースされた最新版で、Jakarta Persistence 3.2仕様をサポートしています。20年以上の開発実績を持つ成熟したライブラリで、Oracle、MySQL、PostgreSQL、SQL Serverなど主要なRDBMSをサポート。スタンドアローンアプリケーションからエンタープライズ環境まで幅広く利用されています。
主な特徴
- Jakarta Persistence 3.2対応: 最新のJPA仕様準拠
- HQL(Hibernate Query Language): SQL風のクエリ言語
- Criteria API: プログラマティックなクエリ構築
- マッピング機能: Javaクラスとデータベーステーブルの自動マッピング
- キャッシュ機能: 1次・2次キャッシュによるパフォーマンス最適化
- トランザクション管理: 宣言的・プログラマティック両対応
メリット・デメリット
メリット
- Java界で最も歴史と実績のあるORM(20年以上の開発実績)
- Jakarta EE標準として多くのアプリケーションサーバーに組み込み済み
- JDBC比較で大幅なコーディング量削減を実現
- データベースの抽象化でベンダー依存性を低減
- 設定の外部化によりアプリケーションコードと分離
- 豊富なドキュメントと成熟したコミュニティ
デメリット
- 学習コストが高く、RDBとSQLの深い知識が必要
- 複雑なクエリではパフォーマンスチューニングが困難
- アンチパターンとしてのORM批判(貧血ドメインモデルの原因)
- 設定が複雑で初心者には敷居が高い
- ランタイム要件(Java 11以上、Jakarta EE移行)が厳しい
- 新しいORM比較でボイラープレートコードが多い
参考ページ
書き方の例
基本的なセットアップ
<!-- Maven依存関係 -->
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.0.0.Final</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
エンティティの定義
import jakarta.persistence.*;
import java.time.LocalDate;
import java.util.List;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
@Column(name = "email", unique = true, nullable = false)
private String email;
@Column(name = "full_name")
private String fullName;
@Column(name = "birth_date")
private LocalDate birthDate;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private UserStatus status;
// One-to-Many関係
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Book> books;
// コンストラクタ
public User() {}
public User(String username, String email, String fullName) {
this.username = username;
this.email = email;
this.fullName = fullName;
this.status = UserStatus.ACTIVE;
}
// ゲッター・セッター
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; }
// その他のゲッター・セッター...
}
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "isbn", unique = true, length = 13)
private String isbn;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Column(name = "publication_date")
private LocalDate publicationDate;
// Many-to-One関係
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private User author;
// コンストラクタとゲッター・セッター...
}
public enum UserStatus {
ACTIVE, INACTIVE, SUSPENDED
}
Hibernateの設定
// persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
version="3.0">
<persistence-unit name="myApp">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.example.entity.User</class>
<class>com.example.entity.Book</class>
<properties>
<!-- データベース接続設定 -->
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/myapp"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value="password"/>
<!-- Hibernate設定 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!-- コネクションプール設定 -->
<property name="hibernate.connection.pool_size" value="10"/>
<property name="hibernate.c3p0.min_size" value="5"/>
<property name="hibernate.c3p0.max_size" value="20"/>
<property name="hibernate.c3p0.timeout" value="1800"/>
<property name="hibernate.c3p0.max_statements" value="50"/>
</properties>
</persistence-unit>
</persistence>
EntityManagerFactoryとEntityManagerの使用
import jakarta.persistence.*;
import java.util.List;
public class HibernateExample {
private static EntityManagerFactory entityManagerFactory;
public static void main(String[] args) {
// EntityManagerFactory初期化
entityManagerFactory = Persistence.createEntityManagerFactory("myApp");
try {
// CRUD操作の実行
createUser();
readUsers();
updateUser();
deleteUser();
} finally {
// リソースのクリーンアップ
entityManagerFactory.close();
}
}
// ユーザー作成
public static void createUser() {
EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
User user = new User("john_doe", "[email protected]", "John Doe");
user.setBirthDate(LocalDate.of(1990, 5, 15));
Book book = new Book();
book.setIsbn("9780123456789");
book.setTitle("Hibernate入門");
book.setDescription("Hibernateの基本的な使い方を学ぶ");
book.setPublicationDate(LocalDate.now());
book.setAuthor(user);
em.persist(user);
em.persist(book);
transaction.commit();
System.out.println("ユーザーが作成されました: " + user.getId());
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
e.printStackTrace();
} finally {
em.close();
}
}
// ユーザー検索
public static void readUsers() {
EntityManager em = entityManagerFactory.createEntityManager();
try {
// 全ユーザー取得
List<User> users = em.createQuery("SELECT u FROM User u", User.class)
.getResultList();
System.out.println("全ユーザー数: " + users.size());
for (User user : users) {
System.out.println("ユーザー: " + user.getUsername() + " (" + user.getEmail() + ")");
}
// IDで検索
User user = em.find(User.class, 1L);
if (user != null) {
System.out.println("見つかったユーザー: " + user.getFullName());
}
} finally {
em.close();
}
}
}
HQLクエリの使用
public class HQLExamples {
// 基本的なHQLクエリ
public List<User> findActiveUsers(EntityManager em) {
return em.createQuery(
"SELECT u FROM User u WHERE u.status = :status ORDER BY u.username",
User.class)
.setParameter("status", UserStatus.ACTIVE)
.getResultList();
}
// JOIN を使ったクエリ
public List<User> findUsersWithBooks(EntityManager em) {
return em.createQuery(
"SELECT DISTINCT u FROM User u JOIN u.books b WHERE b.publicationDate > :date",
User.class)
.setParameter("date", LocalDate.now().minusYears(1))
.getResultList();
}
// 集計クエリ
public Long countBooksByAuthor(EntityManager em, Long authorId) {
return em.createQuery(
"SELECT COUNT(b) FROM Book b WHERE b.author.id = :authorId",
Long.class)
.setParameter("authorId", authorId)
.getSingleResult();
}
// ネイティブSQLクエリ
public List<Object[]> getUserStats(EntityManager em) {
return em.createNativeQuery(
"SELECT u.username, COUNT(b.id) as book_count " +
"FROM users u LEFT JOIN books b ON u.id = b.author_id " +
"GROUP BY u.id, u.username " +
"ORDER BY book_count DESC")
.getResultList();
}
// Named Query
@NamedQuery(
name = "User.findByEmailDomain",
query = "SELECT u FROM User u WHERE u.email LIKE CONCAT('%', :domain)"
)
// エンティティクラスに定義
public List<User> findUsersByEmailDomain(EntityManager em, String domain) {
return em.createNamedQuery("User.findByEmailDomain", User.class)
.setParameter("domain", domain)
.getResultList();
}
}
Criteria APIの使用
import jakarta.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
public class CriteriaExamples {
// 動的クエリの構築
public List<User> findUsersByCriteria(EntityManager em, String username, String email, UserStatus status) {
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 + "%"));
}
if (status != null) {
predicates.add(cb.equal(user.get("status"), status));
}
query.select(user)
.where(predicates.toArray(new Predicate[0]))
.orderBy(cb.asc(user.get("username")));
return em.createQuery(query).getResultList();
}
// JOINを使った複雑なクエリ
public List<User> findUsersWithRecentBooks(EntityManager em, int months) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
Join<User, Book> book = user.join("books");
LocalDate cutoffDate = LocalDate.now().minusMonths(months);
query.select(user)
.distinct(true)
.where(cb.greaterThan(book.get("publicationDate"), cutoffDate))
.orderBy(cb.desc(book.get("publicationDate")));
return em.createQuery(query).getResultList();
}
// 集計クエリ
public Object[] getUserBookStatistics(EntityManager em) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<User> user = query.from(User.class);
Join<User, Book> book = user.join("books", JoinType.LEFT);
query.multiselect(
user.get("username"),
cb.count(book.get("id")),
cb.max(book.get("publicationDate"))
)
.groupBy(user.get("id"), user.get("username"))
.having(cb.greaterThan(cb.count(book.get("id")), 0L))
.orderBy(cb.desc(cb.count(book.get("id"))));
return em.createQuery(query).getResultList().toArray(new Object[0][]);
}
}
トランザクション管理
public class TransactionExamples {
// プログラマティックトランザクション
public void transferBooks(EntityManager em, Long fromUserId, Long toUserId, List<Long> bookIds) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
User fromUser = em.find(User.class, fromUserId);
User toUser = em.find(User.class, toUserId);
if (fromUser == null || toUser == null) {
throw new IllegalArgumentException("ユーザーが見つかりません");
}
for (Long bookId : bookIds) {
Book book = em.find(Book.class, bookId);
if (book != null && book.getAuthor().getId().equals(fromUserId)) {
book.setAuthor(toUser);
em.merge(book);
}
}
transaction.commit();
System.out.println("書籍の移譲が完了しました");
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
System.err.println("トランザクションエラー: " + e.getMessage());
throw e;
}
}
// バッチ処理
public void batchInsertUsers(EntityManager em, List<User> users) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
em.persist(users.get(i));
if (i % batchSize == 0 && i > 0) {
em.flush();
em.clear();
}
}
transaction.commit();
} catch (Exception e) {
if (transaction.isActive()) {
transaction.rollback();
}
throw e;
}
}
}
キャッシュの活用
// エンティティレベルキャッシュ
@Entity
@Table(name = "users")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
// エンティティ定義...
}
// クエリキャッシュ
public List<User> getCachedActiveUsers(EntityManager em) {
return em.createQuery("SELECT u FROM User u WHERE u.status = :status", User.class)
.setParameter("status", UserStatus.ACTIVE)
.setHint("org.hibernate.cacheable", true)
.getResultList();
}
// 2次キャッシュの設定(hibernate.cfg.xml)
```xml
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.jcache.JCacheRegionFactory
</property>