基于 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-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:55