基于 Hibernate ORM 使用 Javalin
介绍
我非常喜欢像 Javalin 这样的框架,它用 Java/Kotlin 构建 Web 应用。我欣赏它对简洁性和易用性的关注。
在本教程中,我将探讨如何将 Javalin 与我的另一个最喜欢的框架Hibernate ORM集成。
新项目
首先,使用带有 Kotlin DSL 的 Gradle创建一个新项目。
在 IntelliJ IDEA 中,这通常通过导航到新建 -> 项目 -> Java -> Gradle DSL(Kotlin)来完成。
Gradle(build.gradle.kts)
我简单地添加了Javalin 包,以及两个 Hibernate 依赖项和一个 PostgreSQL 依赖项。
我在这里也使用 Lombok,但这不是必需的。
plugins {
    id(“java”)
}
group = “com.brucemelo.app”
repositories {
    mavenCentral()
}
val javalinVersion = “6.3.0”
val lpmbokVersion = “1.18.34”
val postgresqlVersion = “42.7.3”
val hibernateVersion = “7.0.0.Beta1”
val junitVersion = “5.10.3”
dependencies {
    implementation(“io.javalin:javalin-bundle:$javalinVersion”)
    compileOnly(“org.projectlombok:lombok:$lpmbokVersion”)
    annotationProcessor(“org.projectlombok:lombok:$lpmbokVersion”)
    implementation(“org.postgresql:postgresql:$postgresqlVersion”)
    implementation(“org.hibernate.orm:hibernate-core:$hibernateVersion”)
    annotationProcessor(“org.hibernate.orm:hibernate-jpamodelgen:$hibernateVersion”)
    testImplementation(platform(“org.junit:junit-bom:$junitVersion”))
    testImplementation(“org.junit.jupiter:junit-jupiter”)
}
tasks.test {
    useJUnitPlatform()
}
Docker 与 PostgreSQL 组合
您需要一个 docker compose 设置来在本地运行 PostgreSQL。
version: ‘3.8’
services:
  postgres:
    container_name: postgres1
    image: postgres:15.7
    environment:
      POSTGRES_USER: sa
      POSTGRES_PASSWORD: sa
      POSTGRES_DB: mydatabase
    ports:
  - "5432:5432"
restart: unless-stoppedHibernate 映射
使用 Hibernate 注解定义一个简单的实体类 Course。
@Setter @Getter
@Entity
@Table
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
public static Course newCourse(String name) {
    var course = new Course();
    course.setName(name);
    return course;
}}
Hibernate 配置/SessionFactory
要使用 Hibernate,我们需要设置其核心组件:Configuration 和SessionFactory。
配置:加载 Hibernate 配置设置的对象。它还负责映射实体类。
SessionFactory:是一个线程安全的工厂,用于创建 Session/StatelessSession 对象。由于它是一个重量级对象,我们通常只需实例化一次。
Hibernate 配置
class AppHibernateConfig {
static Configuration configuration() {
    var configuration = new Configuration();
    var settings = new Properties();
    settings.put(AvailableSettings.JAKARTA_JDBC_DRIVER, "org.postgresql.Driver");
    settings.put(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://localhost:5432/mydatabase");
    settings.put(AvailableSettings.JAKARTA_JDBC_USER, "sa");
    settings.put(AvailableSettings.JAKARTA_JDBC_PASSWORD, "sa");
    settings.put(AvailableSettings.HIGHLIGHT_SQL, true);
    settings.put(AvailableSettings.HBM2DDL_AUTO, Action.ACTION_CREATE);
    configuration.setProperties(settings);
    configuration.addAnnotatedClass(Course.class);
    return configuration;
}}
Hibernate 会话工厂
class AppHibernateSessionFactory {
private static final Logger logger = LoggerFactory.getLogger(AppHibernateSessionFactory.class);
private static SessionFactory sessionFactory;
static SessionFactory getSessionFactory() {
    if (Objects.isNull(sessionFactory)) {
        try {
            var configuration = AppHibernateConfig.configuration();
            var serviceRegistry = new StandardServiceRegistryBuilder()
                    .applySettings(configuration.getProperties())
                    .build();
            sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        } catch (Throwable ex) {
            logger.error("Failed to create session factory", ex);
        }
    }
    return sessionFactory;
}}
Hibernate 无状态会话包装器
我将使用 Hibernate 的无状态会话,原因如下,如官方文档所述:
提供一种面向命令、更裸机的与数据库交互的方法。
没有一级缓存(持久化上下文),也不与任何二级缓存交互,并且
没有实现事务后写或自动脏检查,因此所有操作在明确调用时都会立即执行。
我围绕 Hibernate StatelessSession 和 Transaction 开发了一个轻量级包装器,它将成为项目中其他类的接口 - AppHibernate。
AppHibernateConfig和AppHibernateSessionFactory类被封装,只能在包内访问。
public class AppHibernate {
public static void inTransaction(Consumer<StatelessSession> consumer) {
    AppHibernateConfig.getSessionFactory().inStatelessTransaction(consumer);
}
public static <R> R fromTransaction(Function<StatelessSession, R> function) {
    return AppHibernateConfig.getSessionFactory().fromStatelessTransaction(function);
}}
Javalin 处理程序
配置完 Hibernate 后,我们可以创建一个 CourseHandler 来管理与课程相关的 HTTP 请求。该处理程序将利用 Javalin 的方法,使用 Context 对象。
为了与数据库交互,CourseHandler 将使用AppHibernate包装器。该包装器通过管理 Hibernate 事务来简化数据访问。
public class CourseHandler {
public static Handler listAll = (context) -> {
    var result = AppHibernate.fromTransaction(CourseQueries_::getAllCourses);
    context.json(new ResultCourse(result));
};
public static Handler save = (context) -> {
    var newCourse = context.bodyAsClass(NewCourse.class);
    var result = AppHibernate.fromTransaction(session -> {
        var insertedId = session.insert(Course.newCourse(newCourse.name()));
        return session.get(Course.class, insertedId);
    });
    context.json(result).status(HttpStatus.CREATED);
};}
这种结构通过分离关注点来提升代码的简洁性:CourseHandler 专注于请求处理,而 AppHibernate 负责处理 Hibernate 事务。这种分离改善了代码的组织性和可维护性。
JavalinApp 和 Main
最后,我们有 Javalin 应用程序配置和主类。
public class JavalinApp {
public static Javalin create() {
    return Javalin.create((var config) -> config.router.apiBuilder(() -> {
        path("/", () -> get(ctx -> ctx.json("Ok")));
        path("/courses", () -> {
            get(CourseHandler.listAll);
            post(CourseHandler.save);
        });
    }));
}}
public class Main {
public static void main(String[] args) {
    JavalinApp.create().start(8080);
}}
测试
使用 JUnit 5 进行测试的示例。
class CoursesTest {
Javalin app = JavalinApp.create();
JavalinJackson javalinJackson = new JavalinJackson();
@Test
@DisplayName("Should save and list courses")
void test1() {
    JavalinTest.test(app, (server, client) -> {
        var newCourse = new NewCourse("Course1");
        var postResponse = client.post("/courses", newCourse);
        assertEquals(postResponse.code(), HttpStatus.CREATED.getCode());
        var response = client.get("/courses");
        assertEquals(response.code(), HttpStatus.OK.getCode());
        assertNotNull(response.body());
        ResultCourse result = javalinJackson.fromJsonString(response.body().string(), ResultCourse.class);
        assertNotNull(result.courses());
        var firstCourse = result.courses().stream().findFirst();
        assertTrue(firstCourse.isPresent());
        assertEquals(firstCourse.get().getName(), newCourse.name());
    });
}}
结论
我们可以看到,Javalin 和 Hibernate 无缝集成,实现了优雅高效的实现,避免了不必要的冗长。Java 21、Javalin 和 Hibernate 7 的结合,为构建现代 Web 应用程序提供了强大而高效的技术栈。
对于生产就绪设置,建议配置一个强大的连接池,例如 HikariCP 或 Agroal。
最后编辑:Jeebiz 更新时间:2025-05-04 00:55
