介绍
我非常喜欢像 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-stopped

Hibernate 映射
使用 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:54
最后编辑:Jeebiz  更新时间:2025-05-04 00:55