我是 hibernate-reactive、smallrye mutini、PanacheEntity、PanacheRepository 的新手。
在高水平上我有:
租户 - 域对象(我不想在其中包含与数据库相关的代码)
TenantDAO - 特定于数据库的 dao 对象
SqlRepository - 存储库的基类,用于存储 dao 并映射 dao <-> 实体
TenantRepository - 扩展 SqlRepository 并提供 TenantDAO 对象
POST 的休息处理程序,调用存储库来存储租户信息。
这是我的代码:
Tenant.java(域实体)
package com.my.platform.model.user;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class Tenant {
private String id; // an application specific id (not the PanacheEntity id)
private String name;
}
TenantDAO.java(存储在数据库中的对象):
package com.my.platform.repository.user;
import com.my.platform.model.user.Tenant;
import io.quarkus.hibernate.reactive.panache.PanacheEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "Tenants")
public class TenantDAO extends PanacheEntity {
private String entityId;
private String name;
public TenantDAO(Tenant tenant) {
this.entityId = tenant.getId();
this.shortName = tenant.getShortName();
}
public Tenant getEntity() {
return new Tenant(entityId, name);
}
}
我在同一个旧版本中也有这个文件:
package-info.java
@io.quarkus.hibernate.orm.PersistenceUnit("<default>")
package com.my.platform.repository.user;
TenantRepository.java:
package com.my.platform.repository.user;
import com.my.platform.model.user.Tenant;
import com.mys.platform.repository.impl.sql.SqlRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.ext.Provider;
@ApplicationScoped
public class TenantRepository extends SqlRepository<Tenant, TenantDAO> {
@Override
public TenantDAO createDAO(Tenant tenant) {
return new TenantDAO(tenant);
}
}
Sql存储库:
package com.my.platform.repository.impl.sql;
import java.util.List;
import io.quarkus.hibernate.reactive.panache.PanacheRepository;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
@ApplicationScoped
public abstract class SqlRepository<E, DAO> implements PanacheRepository<DAO> {
@Override
@WithSession
public Uni<List<E>> list() {
return listAll().onItem().transform(entityList ->
entityList.stream().map(entityDao -> entityDao.getEntity()).toList()
);
}
@Override
@WithSession
public Uni<E> findByEntityId(String id) {
return find("entityId", id).firstResult().map(dao -> (E)dao.getEntity());
}
@Override
@Transactional
public Uni<E> add(E entity) {
DAO dao = createDAO(entity);
persist(dao);
return Uni.createFrom().item(dao.getEntity());
}
abstract protected DAO createDAO(E);
}
REST资源方法(TenantCreationData是一个简单的POJO,用于获取租户信息):
@POST
@Transactional
public Uni<Response> provision(TenantCreationData tenantCreationData) {
Tenant tenant = new Tenant(tenantCreationData.getTenantName());
return tenantRepository.add(tenant)
.onItem()
.transform(addedTenant -> (addedTenant != null) ? Response.ok(addedTenant) : Response.status(Response.Status.NOT_FOUND))
.onItem()
.transform(Response.ResponseBuilder::build);
}
注:
我不完全理解@WithSession,因为我找不到很好的文档来准确解释这个会话是什么。我通过将此注释添加到 get() 方法来使代码正常工作(否则它会因某些会话中继错误而出错)。如果我向 add() 方法添加相同的 @WithSession 注释,那么我会收到一个在谷歌搜索中找不到的模糊错误 -
Error id 1aff6b15-313d-47a9-9422-97afdca21e95-1, java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-1'
无论如何,上面显示的代码不会给我任何错误,但是当向它发送 HTTP 请求时会遍历包括 persist() 在内的所有代码,但它不会保存到数据库中。
我不确定这是否与会话有关—— persist() 不会抛出任何异常,但数据不会保存。与 persistAndFlush() 相同。我调试到 persist() 并深入到 AbstractJpaOperations 的 persist() 方法内部, if(!session.contains(entity)) 处的断点没有命中(见下文)。在会话处理或“会话链”方面失败?我不确定我是否完全理解这里的课程。
public Uni<Void> persist(Uni<Mutiny.Session> sessionUni, Object entity) {
return sessionUni.chain(session -> {
if (!session.contains(entity)) {
return session.persist(entity);
}
return Uni.createFrom().nullItem();
});
}
应用程序属性:
# DataSource configuration
quarkus.datasource.url=<mysql db>
quarkus.datasource.driver=com.mysql.cj.jdbc.Driver
quarkus.datasource.username=<username>
quarkus.datasource.password=<password>
# Default persistence unit
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.datasource=<default>
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.multitenant=SCHEMA
# Tenant persistence unit
quarkus.hibernate-orm."tenant".database.generation=none
quarkus.hibernate-orm."tenant".datasource=<default>
quarkus.hibernate-orm."tenant".log.sql=true
quarkus.hibernate-orm."tenant".multitenant=SCHEMA
# Hibernate ORM configuration
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.database.default-schema=shared
多租户配置类:
@PersistenceUnitExtension
@RequestScoped
public class DefaultTenantResolver implements TenantResolver {
private static final String DEFAULT_TENANT = "shared";
@Override
public String getDefaultTenantId() {
return DEFAULT_TENANT;
}
@Override
public String resolveTenantId() {
return DEFAULT_TENANT;
}
}
@PersistenceUnitExtension("tenant")
@RequestScoped
public class CustomTenantResolver implements TenantResolver {
private static final String DEFAULT_TENANT = "shared";
@Inject
RoutingContext context;
@Inject
UserService userService;
@Override
public String getDefaultTenantId() {
return DEFAULT_TENANT;
}
@Override
public String resolveTenantId() {
String tenantName = context.request().getHeader("tenant");
return tenantName;
}
}
对我所缺少的有什么想法吗?
当我发布到我的资源时,我希望保存数据。但没有保存。
解决了!
改变了这一点: @覆盖 @事务性 公共Uni add(E实体){ DAO dao = createDAO(实体); 坚持(dao); return Uni.createFrom().item(dao.getEntity()); }
至:
@Override
public Uni<E> add(E entity) {
DAO dao = createDAO(entity);
return persist(dao).onItem().transform(addedDao -> (addedDao != null) ? dao.getEntity() : null);
}