Hibernate ORM

HibernateはJava界のデファクトスタンダードORMライブラリです。O/Rマッパー(Object-Relational Mapping)として、オブジェクト指向のJavaオブジェクトとリレーショナルデータベースを巧妙に関連付けるツールです。Jakarta EE(旧Java EE)の一部として標準化され、HQL(Hibernate Query Language)やCriteria APIによる柔軟なデータアクセスを提供します。

ORMJavaJakarta EEデータベースJPAHQL

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

スター履歴

hibernate/hibernate-orm Star History
データ取得日時: 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>