Hibernate ORM (Kotlin)

Hibernate ORM is "a comprehensive object-relational mapping solution for Java and Kotlin" developed as the most mature ORM framework in the JVM ecosystem. With over 20 years of development experience, it enables large-scale data processing in complex enterprise applications. As the reference implementation of JPA (Jakarta Persistence API), it combines standard JPA APIs with proprietary extensions to provide type-safe database operations fully integrated with Kotlin language features. Through deep integration with modern frameworks like Spring Boot and Quarkus, it has established a solid position as the foundation for contemporary Kotlin application development.

ORMKotlinJavaJPADatabaseEnterprise

GitHub Overview

hibernate/hibernate-orm

Hibernate's core Object/Relational Mapping functionality

Stars6,188
Watchers303
Forks3,649
Created:October 4, 2010
Language:Java
License:Apache License 2.0

Topics

databaseenvershibernatehibernate-ormjakarta-persistencejakartaeejavajdbcjpaobject-relational-mapperobject-relational-mappingormpersistencepersistence-frameworksql

Star History

hibernate/hibernate-orm Star History
Data as of: 7/17/2025, 02:30 AM

Library

Hibernate ORM (Kotlin)

Overview

Hibernate ORM is "a comprehensive object-relational mapping solution for Java and Kotlin" developed as the most mature ORM framework in the JVM ecosystem. With over 20 years of development experience, it enables large-scale data processing in complex enterprise applications. As the reference implementation of JPA (Jakarta Persistence API), it combines standard JPA APIs with proprietary extensions to provide type-safe database operations fully integrated with Kotlin language features. Through deep integration with modern frameworks like Spring Boot and Quarkus, it has established a solid position as the foundation for contemporary Kotlin application development.

Details

Hibernate ORM 2025 edition provides next-generation ORM experience optimized for Kotlin development. Full support for Jakarta Persistence 3.0 provides the latest Java EE standards, and integration with Kotlin Coroutines allows natural handling of asynchronous processing. In addition to Hibernate's Criteria API, HQL, and native SQL queries, type-safe query construction through Kotlin extension functions is possible. It comes standard with enterprise-level performance features such as second-level caching, lazy loading, and batch processing optimization, achieving complete support for major databases including PostgreSQL, MySQL, Oracle, and SQL Server.

Key Features

  • Jakarta Persistence Standard: JPA 3.0 compliant standardized ORM operations
  • Kotlin Native Support: Deep integration with Kotlin language features and type safety
  • Enterprise-Level Features: Second-level caching, connection pooling, transaction management
  • Flexible Queries: Comprehensive support for HQL, Criteria API, and native SQL
  • Performance Optimization: Lazy loading, batch processing, N+1 problem solutions
  • Broad Database Support: Complete support for major RDBMS systems

Pros and Cons

Pros

  • High stability and rich feature set due to over 20 years of track record
  • Rich portability and learning resources due to JPA standard compliance
  • Excellent integration with ecosystems like Spring Boot and Quarkus
  • Enterprise-level performance optimization features
  • Comprehensive documentation and active community support
  • Improved development efficiency through affinity with Kotlin

Cons

  • High learning cost and complex configuration required for beginners
  • Excessive features and overhead for lightweight applications
  • Configuration and annotations tend to be complex with much boilerplate
  • Magic behavior can make debugging difficult in some cases
  • Large memory usage, unsuitable for small projects
  • Reliance on native queries can compromise portability

Reference Pages

Code Examples

Setup

// build.gradle.kts (Kotlin DSL)
dependencies {
    implementation("org.hibernate.orm:hibernate-core:6.4.0.Final")
    implementation("org.hibernate.orm:hibernate-hikaricp:6.4.0.Final")
    implementation("jakarta.persistence:jakarta.persistence-api:3.1.0")
    
    // Database drivers
    implementation("org.postgresql:postgresql:42.7.1")
    // or implementation("mysql:mysql-connector-java:8.0.33")
    
    // Kotlin support
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    
    // Spring Boot integration
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
}
// hibernate.properties or application.yml
// hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
// hibernate.connection.driver_class=org.postgresql.Driver
// hibernate.connection.url=jdbc:postgresql://localhost:5432/mydb
// hibernate.connection.username=user
// hibernate.connection.password=password
// hibernate.hbm2ddl.auto=update
// hibernate.show_sql=true

Basic Usage

import jakarta.persistence.*
import org.hibernate.annotations.GenericGenerator
import java.time.LocalDateTime

// Entity class definition
@Entity
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
    
    @Column(nullable = false, length = 100)
    val name: String,
    
    @Column(unique = true, nullable = false)
    val email: String,
    
    @Column(name = "created_at")
    val createdAt: LocalDateTime = LocalDateTime.now(),
    
    @Column(name = "is_active")
    val isActive: Boolean = true,
    
    // Relationships
    @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
    val posts: MutableList<Post> = mutableListOf()
) {
    // JPA requires default constructor
    constructor() : this(name = "", email = "")
}

@Entity
@Table(name = "posts")
data class Post(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
    
    @Column(nullable = false)
    val title: String,
    
    @Column(columnDefinition = "TEXT")
    val content: String,
    
    @Column(name = "published_at")
    val publishedAt: LocalDateTime? = null,
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    val user: User,
    
    @ElementCollection
    @CollectionTable(name = "post_tags", joinColumns = [JoinColumn(name = "post_id")])
    @Column(name = "tag")
    val tags: MutableSet<String> = mutableSetOf()
) {
    constructor() : this(title = "", content = "", user = User())
}

Query Execution

import jakarta.persistence.EntityManager
import jakarta.persistence.EntityManagerFactory
import jakarta.persistence.Persistence
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.Root

class UserRepository(private val entityManager: EntityManager) {
    
    // Basic CRUD operations
    fun save(user: User): User {
        return entityManager.merge(user)
    }
    
    fun findById(id: Long): User? {
        return entityManager.find(User::class.java, id)
    }
    
    fun findAll(): List<User> {
        val criteriaBuilder = entityManager.criteriaBuilder
        val criteriaQuery = criteriaBuilder.createQuery(User::class.java)
        val root = criteriaQuery.from(User::class.java)
        criteriaQuery.select(root)
        
        return entityManager.createQuery(criteriaQuery).resultList
    }
    
    fun delete(user: User) {
        entityManager.remove(entityManager.merge(user))
    }
    
    // JPQL queries
    fun findByEmail(email: String): User? {
        return entityManager.createQuery(
            "SELECT u FROM User u WHERE u.email = :email", 
            User::class.java
        ).setParameter("email", email)
         .resultList
         .firstOrNull()
    }
    
    fun findActiveUsers(): List<User> {
        return entityManager.createQuery(
            "SELECT u FROM User u WHERE u.isActive = true ORDER BY u.createdAt DESC",
            User::class.java
        ).resultList
    }
    
    // Criteria API (type-safe)
    fun findUsersByNamePattern(namePattern: String): List<User> {
        val cb = entityManager.criteriaBuilder
        val cq = cb.createQuery(User::class.java)
        val root = cq.from(User::class.java)
        
        cq.select(root)
        cq.where(cb.like(cb.lower(root.get("name")), "%${namePattern.lowercase()}%"))
        cq.orderBy(cb.asc(root.get<String>("name")))
        
        return entityManager.createQuery(cq).resultList
    }
    
    // Native SQL queries
    fun getUserStatistics(): List<Map<String, Any>> {
        val query = entityManager.createNativeQuery("""
            SELECT 
                DATE_TRUNC('month', created_at) as month,
                COUNT(*) as user_count,
                COUNT(CASE WHEN is_active THEN 1 END) as active_count
            FROM users 
            GROUP BY DATE_TRUNC('month', created_at)
            ORDER BY month DESC
        """)
        
        @Suppress("UNCHECKED_CAST")
        return query.resultList as List<Array<Any>>
            .map { row ->
                mapOf(
                    "month" to row[0],
                    "userCount" to row[1],
                    "activeCount" to row[2]
                )
            }
    }
}

Data Operations

import jakarta.persistence.EntityTransaction

class UserService(private val userRepository: UserRepository) {
    
    fun createUser(name: String, email: String): User {
        val user = User(name = name, email = email)
        return userRepository.save(user)
    }
    
    fun createUserWithPosts(
        name: String, 
        email: String, 
        postTitles: List<String>
    ): User {
        val user = User(name = name, email = email)
        
        postTitles.forEach { title ->
            val post = Post(
                title = title,
                content = "Initial content for $title",
                user = user
            )
            user.posts.add(post)
        }
        
        return userRepository.save(user)
    }
    
    fun updateUserActivity(userId: Long, isActive: Boolean): User? {
        val user = userRepository.findById(userId) ?: return null
        val updatedUser = user.copy(isActive = isActive)
        return userRepository.save(updatedUser)
    }
    
    fun searchUsers(searchTerm: String): List<User> {
        return userRepository.findUsersByNamePattern(searchTerm)
    }
}

// Transaction management example
class TransactionalUserService(
    private val entityManagerFactory: EntityManagerFactory
) {
    
    fun performBulkUserOperation(userUpdates: List<UserUpdate>): Result<Unit> {
        val entityManager = entityManagerFactory.createEntityManager()
        val transaction = entityManager.transaction
        
        return try {
            transaction.begin()
            
            userUpdates.forEach { update ->
                val user = entityManager.find(User::class.java, update.userId)
                    ?: throw IllegalArgumentException("User not found: ${update.userId}")
                
                val updatedUser = user.copy(
                    name = update.newName ?: user.name,
                    email = update.newEmail ?: user.email,
                    isActive = update.newActiveStatus ?: user.isActive
                )
                
                entityManager.merge(updatedUser)
            }
            
            transaction.commit()
            Result.success(Unit)
            
        } catch (e: Exception) {
            if (transaction.isActive) {
                transaction.rollback()
            }
            Result.failure(e)
        } finally {
            entityManager.close()
        }
    }
}

data class UserUpdate(
    val userId: Long,
    val newName: String? = null,
    val newEmail: String? = null,
    val newActiveStatus: Boolean? = null
)

Configuration and Customization

// Custom converter implementation
import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json

@Converter(autoApply = true)
class StringListConverter : AttributeConverter<List<String>, String> {
    
    override fun convertToDatabaseColumn(attribute: List<String>?): String? {
        return attribute?.let { Json.encodeToString(it) }
    }
    
    override fun convertToEntityAttribute(dbData: String?): List<String>? {
        return dbData?.let { Json.decodeFromString<List<String>>(it) }
    }
}

// Custom entity example
@Entity
@Table(name = "user_profiles")
data class UserProfile(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,
    
    @OneToOne
    @JoinColumn(name = "user_id", unique = true)
    val user: User,
    
    @Column(name = "bio", columnDefinition = "TEXT")
    val bio: String? = null,
    
    @Convert(converter = StringListConverter::class)
    @Column(name = "interests")
    val interests: List<String> = emptyList(),
    
    @Embedded
    val address: Address? = null,
    
    @ElementCollection
    @CollectionTable(name = "user_social_links", joinColumns = [JoinColumn(name = "profile_id")])
    @MapKeyColumn(name = "platform")
    @Column(name = "url")
    val socialLinks: MutableMap<String, String> = mutableMapOf()
) {
    constructor() : this(user = User())
}

@Embeddable
data class Address(
    @Column(name = "street")
    val street: String = "",
    
    @Column(name = "city")
    val city: String = "",
    
    @Column(name = "postal_code")
    val postalCode: String = "",
    
    @Column(name = "country")
    val country: String = ""
)

// Repository pattern implementation
interface UserProfileRepository {
    fun save(profile: UserProfile): UserProfile
    fun findByUserId(userId: Long): UserProfile?
    fun findProfilesWithInterest(interest: String): List<UserProfile>
    fun updateBio(userId: Long, bio: String): Boolean
}

class HibernateUserProfileRepository(
    private val entityManager: EntityManager
) : UserProfileRepository {
    
    override fun save(profile: UserProfile): UserProfile {
        return entityManager.merge(profile)
    }
    
    override fun findByUserId(userId: Long): UserProfile? {
        return entityManager.createQuery(
            "SELECT p FROM UserProfile p WHERE p.user.id = :userId",
            UserProfile::class.java
        ).setParameter("userId", userId)
         .resultList
         .firstOrNull()
    }
    
    override fun findProfilesWithInterest(interest: String): List<UserProfile> {
        // JSON function search (PostgreSQL example)
        return entityManager.createQuery(
            "SELECT p FROM UserProfile p WHERE JSON_EXTRACT(p.interests, '$') LIKE :interest",
            UserProfile::class.java
        ).setParameter("interest", "%$interest%")
         .resultList
    }
    
    override fun updateBio(userId: Long, bio: String): Boolean {
        val rowsAffected = entityManager.createQuery(
            "UPDATE UserProfile p SET p.bio = :bio WHERE p.user.id = :userId"
        ).setParameter("bio", bio)
         .setParameter("userId", userId)
         .executeUpdate()
        
        return rowsAffected > 0
    }
}

Error Handling

import jakarta.persistence.EntityNotFoundException
import jakarta.persistence.PersistenceException
import jakarta.persistence.RollbackException
import org.hibernate.exception.ConstraintViolationException

sealed class DatabaseError : Exception() {
    object UserNotFound : DatabaseError()
    data class ConstraintViolation(val constraintName: String) : DatabaseError()
    data class PersistenceError(override val message: String) : DatabaseError()
    data class UnknownError(val cause: Throwable) : DatabaseError()
}

class SafeUserService(
    private val entityManagerFactory: EntityManagerFactory
) {
    
    fun createUserSafely(name: String, email: String): Result<User> {
        return executeInTransaction { entityManager ->
            val existingUser = entityManager.createQuery(
                "SELECT u FROM User u WHERE u.email = :email",
                User::class.java
            ).setParameter("email", email)
             .resultList
             .firstOrNull()
            
            if (existingUser != null) {
                throw IllegalArgumentException("User with email $email already exists")
            }
            
            val user = User(name = name, email = email)
            entityManager.persist(user)
            user
        }
    }
    
    fun updateUserSafely(userId: Long, updates: UserUpdate): Result<User> {
        return executeInTransaction { entityManager ->
            val user = entityManager.find(User::class.java, userId)
                ?: throw EntityNotFoundException("User not found: $userId")
            
            val updatedUser = user.copy(
                name = updates.newName ?: user.name,
                email = updates.newEmail ?: user.email,
                isActive = updates.newActiveStatus ?: user.isActive
            )
            
            entityManager.merge(updatedUser)
        }
    }
    
    private fun <T> executeInTransaction(operation: (EntityManager) -> T): Result<T> {
        val entityManager = entityManagerFactory.createEntityManager()
        val transaction = entityManager.transaction
        
        return try {
            transaction.begin()
            val result = operation(entityManager)
            transaction.commit()
            Result.success(result)
            
        } catch (e: ConstraintViolationException) {
            if (transaction.isActive) transaction.rollback()
            Result.failure(DatabaseError.ConstraintViolation(e.constraintName ?: "unknown"))
            
        } catch (e: EntityNotFoundException) {
            if (transaction.isActive) transaction.rollback()
            Result.failure(DatabaseError.UserNotFound)
            
        } catch (e: RollbackException) {
            Result.failure(DatabaseError.PersistenceError("Transaction rolled back: ${e.message}"))
            
        } catch (e: PersistenceException) {
            if (transaction.isActive) transaction.rollback()
            Result.failure(DatabaseError.PersistenceError(e.message ?: "Unknown persistence error"))
            
        } catch (e: Exception) {
            if (transaction.isActive) transaction.rollback()
            Result.failure(DatabaseError.UnknownError(e))
            
        } finally {
            entityManager.close()
        }
    }
}

// Spring Boot integration example
@Service
@Transactional
class SpringUserService(
    @PersistenceContext
    private val entityManager: EntityManager
) {
    
    @Transactional(readOnly = true)
    fun findAllUsers(): List<User> {
        return entityManager.createQuery("SELECT u FROM User u", User::class.java)
            .resultList
    }
    
    @Transactional
    fun createUser(name: String, email: String): User {
        val user = User(name = name, email = email)
        entityManager.persist(user)
        return user
    }
    
    @Transactional
    fun updateUser(id: Long, updates: UserUpdate): User {
        val user = entityManager.find(User::class.java, id)
            ?: throw EntityNotFoundException("User not found: $id")
        
        val updatedUser = user.copy(
            name = updates.newName ?: user.name,
            email = updates.newEmail ?: user.email,
            isActive = updates.newActiveStatus ?: user.isActive
        )
        
        return entityManager.merge(updatedUser)
    }
}