防止“持久对象异常”

2024-03-15

我有一个非常基本的 JAX-RS 服务(BookService下面的类)允许创建类型的实体Book(也见下文)。POST有效负载

{
    "acquisitionDate": 1418849700000,
    "name": "Funny Title",
    "numberOfPages": 100
}

成功地坚持了Book并返回201 CREATED。然而,包括一个id有效负载上具有任何非空值的属性都会触发org.hibernate.PersistentObjectException与消息detached entity passed to persist. 我明白这意味着什么,并包括一个id创建对象时(在本例中)在有效负载上没有任何意义。但是,我更愿意防止此异常一直冒泡,并向我的用户展示,例如400 BAD REQUEST在这种情况下(或者至少完全忽略该属性)。然而,有两个主要问题:

  1. 到达的异常create is an EJBTransactionRolledbackException我必须沿着堆栈跟踪一路爬行才能发现根本原因;
  2. 根本原因是org.hibernate.PersistentObjectException- 我正在部署到使用 Hibernate 的 Wildfly,但我想保持我的代码可移植,所以我真的不想捕获这个特定的异常。

据我了解,有两种可能的解决方案:

  1. Use book.setId(null) before bookRepo.create(book)。这会忽略这样一个事实:id属性携带一个值并继续请求。
  2. 检查是否book.getId() != null并抛出类似的东西IllegalArgumentException可以映射到400状态码。似乎是更好的解决方案。

然而,来自其他框架(例如 Django Rest Framework),我真的更喜欢由框架本身来处理这个问题...我的问题是,是否有任何内置方法可以实现我可以实现的这种行为失踪了吗?

这是BookService class:

@Stateless
@Path("/books")
public class BookService {
    @Inject
    private BookRepo bookRepo;

    @Context
    UriInfo uriInfo;

    @Consumes(MediaType.APPLICATION_JSON)
    @Path("/")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public Response create(@Valid Book book) {
        bookRepo.create(book);
        return Response.created(getBookUri(book)).build();
    }

    private URI getBookUri(Book book) {
        return uriInfo.getAbsolutePathBuilder()
                .path(book.getId().toString()).build();
    }
}

这是Book class:

@Entity
@Table(name = "books")
public class Book {
    @Column(nullable = false)
    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    private Date acquisitionDate;

    @Column(nullable = false, updatable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Integer id;

    @Column(nullable = false)
    @NotNull
    @Size(max = 255, min = 1)
    private String name;

    @Column(nullable = false)
    @Min(value = 1)
    @NotNull
    private Integer numberOfPages;

    (getters/setters/...)
}

这是BookRepo class:

@Stateless
public class BookRepo {
    @PersistenceContext(unitName = "book-repo")
    protected EntityManager em;

    public void create(Book book) {
        em.persist(book);
    }
}

我不知道这是否真的是您正在寻找的答案,但我只是在尝试这个想法并实现了一些东西。

JAX-RS 2 规范定义了 bean 验证的模型,所以我想也许您可以利用它。所有错误的验证都会映射到 400。您说过“我希望防止此异常一直冒泡,并向我的用户显示,例如 400 BAD REQUEST”,但是如果验证不好,你无论如何都会得到它。因此,无论您计划如何处理验证异常(如果有的话),您都可以在此处执行相同的操作。

基本上我只是创建了一个约束注释来验证 id 字段中的空值。您可以通过以下方式在注释中定义 id 字段的名称idField注释属性,所以你不限于id。此外,这也可以用于其他对象,因此您不必像第二个解决方案中所建议的那样重复检查该值。

你可以玩玩它。只是想我会把这个选项扔掉。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;

@Constraint(validatedBy = NoId.NoIdValidator.class)
@Target({ElementType.PARAMETER})
@Retention(RUNTIME)
public @interface NoId {

    String message() default "Cannot have value for id attribute";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String idField() default "id";

    public static class NoIdValidator implements ConstraintValidator<NoId, Object> {
        private String idField;

        @Override
        public void initialize(NoId annotation) {
            idField = annotation.idField();
        }

        @Override
        public boolean isValid(Object bean, ConstraintValidatorContext cvc) {

            boolean isValid = false;
            try {
                Field field = bean.getClass().getDeclaredField(idField);
                if (field == null) {
                    isValid = true;
                } else {
                    field.setAccessible(true);
                    Object value = field.get(bean);
                    if (value == null) {
                        isValid = true;
                    }
                }
            } catch (NoSuchFieldException 
                    | SecurityException 
                    | IllegalArgumentException 
                    | IllegalAccessException ex) {
                Logger.getLogger(NoId.class.getName()).log(Level.SEVERE, null, ex);
            }
            return isValid;
        }
    }
}

Usage:

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createBook(@Valid @NoId(idField = "id") Book book) {
    book.setId(1);
    return Response.created(URI.create("http://blah.com/books/1"))
            .entity(book).build();
}

注意默认的idField is id,所以如果你不指定它,它会寻找id对象类中的字段。您还可以指定message就像任何其他约束注释一样:

@NoId(idField = "bookId", message = "bookId must not be specified")
                          // default "Cannot have value for id attribute"
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

防止“持久对象异常” 的相关文章

  • 如何将变量的全部内容发送/导出到文本文件/xml 文件/剪贴板?

    我想将实例的内容 最好以树形形式 发送给某人 打印屏幕是不行的 因为类太复杂了 您需要将输出转回实例吗 在这种情况下 其他答案都是正确的 如果您只想手动检查实例的内容 理想情况下您的类都将实现toString 你可以将其重定向到一个文件 如
  • 在命令行java中突出显示文本[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一项任务是重新创建 unix cal 程序 除了一部分之外 相当简单 今天 它突出显示了该数字 我不知道该怎么做 关于如何在 Ja
  • 在 Java 中从 SOAPMessage 获取原始 XML

    我已经在 J AX WS 中设置了 SOAP WebServiceProvider 但我无法弄清楚如何从 SOAPMessage 或任何 Node 对象获取原始 XML 下面是我现在获得的代码示例 以及我试图获取 XML 的位置 WebSe
  • JTextField 和 JTextArea

    JTextField 和 JTextArea 有什么不同 是否可以在一个班级中使用这两个班级 总之 JTextField 是单行文本字段 而 JTextArea 可以跨越多行 文档中清楚地解释了这些差异 文本区 http docs orac
  • java中的单链表和双向链表?

    在java中 哪个集合接口可以有效地实现单链表和双向链表 请问代码示例吗 毫不奇怪 实现双向链表的正确接口是 LinkedList 看Java文档 http docs oracle com javase 8 docs api java ut
  • 使用 JAX-WS 的 WebLogic 中没有模式导入的单个 WSDL

    如何使用 JAX WS 配置由 WebLogic 10 3 6 生成的 Web 服务 以将对象架构包含在单个 WSDL 文件声明 而不是导入声明 中 示例代码 界面 import javax ejb Local Local public i
  • 正则表达式在 Velocity 模板中不起作用

    我在 Test java 中尝试过这个 String regex lt s br s s gt String test1 lt br gt System out println test replaceAll regex 但是当我在速度模板
  • LocalDate 减去 period 得到错误的结果

    LocalDate减去一个Period 如 28年1个月27天 得到错误的结果 但减去一个Period 只有天单位 如 10282 天 得到正确的结果 有什么需要注意的吗 public static void main String arg
  • 将现有 eclipse 项目导出到 war 文件时出现“模块名称无效”

    我正在尝试将现有 Eclipse 项目导出到 war 文件 但无论我在 WAR Export 对话框页面中输入什么 系统总是返回 模块名称无效 我不知道如何解决这个问题 谢谢您的帮助 我有同样的问题 我修复了它 请按照以下步骤操作 您可以创
  • 从 HttpClient 3 转换为 4

    我已经成功地对所有内容进行了更改 但以下内容除外 HttpClient client HttpPost method client new DefaultHttpClient method new HttpPost url InputStr
  • Android 解析 JSON 卡在 get 任务上

    我正在尝试解析一些 JSON 数据 我的代码工作了一段时间 我不确定我改变了什么突然破坏了代码 当我运行代码时 我没有收到任何运行时错误或警告 我创建一个新的 AsyncTask 并执行它 当我打电话时 get 在这个新任务中 调试器在此行
  • 合并两个地图的最佳实践是什么

    如何将新地图添加到现有地图 地图具有相同的类型Map
  • 配置jmxremote时无法正常停止tomcat

    我添加了一个jmxremotecatalina bat中的配置 set JAVA OPTS Dcom sun management jmxremote port 9004 Dcom sun management jmxremote ssl
  • 如何检查日期字符串的有效性?

    在我的项目中 我需要检查日期字符串是否计算为正确的日期对象 我决定允许 yyyy MM dd 和日期格式 年 月 日 和 年 月 日 小时 分钟 我如何检查它们是否有效 我的代码为 1980 01 01 和一些奇怪的日期 如 3837 05
  • 在循环中按名称访问变量

    我正在开发一个 Android 项目 并且有很多可绘制对象 这些绘图的名称都类似于icon 0 png icon 1 png icon 100 png 我想将这些可绘制对象的所有资源 ID 添加到整数 ArrayList 中 对于那些不了解
  • Java和手动执行finalize

    如果我打电话finalize 在我的程序代码中的一个对象上 JVM当垃圾收集器处理这个对象时仍然再次运行该方法吗 这是一个大概的例子 MyObject m new MyObject m finalize m null System gc 是
  • 为什么我不能将 Collection 转换为 Collection>

    问题的关键是 为什么这会导致编译时错误 List
  • 如何使用注释处理 Hibernate 和 Spring 中的连接查询?

    我正在使用 Spring 和 Hibernate 以及 MySQL 开发应用程序 我是 Hibernate 新手 完成了基本任务 现在我需要在选择查询中应用联接以使用注释从多个表中获取数据 我已经搜索过但仍然没有任何想法 这是我的数据库表和
  • 如何使用 Jest 从 ElasticSearch 获取索引列表

    我正在尝试使用 Jest 检索索引列表 但我只得到 Stats statistics new Stats Builder build result client execute statistics 如何从结果中检索索引列表 除了统计之外
  • 将数组值导出到 csv 文件 java

    我只需要帮助将数组元素导出到 csv 文件 我不知道我的代码有什么问题 任何帮助将不胜感激 谢谢 for int index 0 index lt cols length index FileWriter fw new FileWriter

随机推荐