当字符串的长度超过列长度定义时,如何在存储字符串时静默截断字符串?

2022-09-01 05:13:22

我有一个Web应用程序,使用EclipseLink和MySQL来存储数据。其中一些数据是字符串,即数据库中的变量。在实体代码中,字符串具有如下属性:

@Column(name = "MODEL", nullable = true, length = 256)
private String model;

数据库不是由 eclipseLink 从代码中创建的,但长度与 DB 中的 varchar 长度匹配。当此类字符串数据的长度大于 length 属性时,在调用 javax.persistence.EntityTransaction.commit() 期间会引发异常:

javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.0.v20100614-r7608): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'MODEL' at row 1

然后回滚事务。虽然我知道这是默认行为,但这不是我想要的。我希望以静默方式截断数据,并提交事务。

我是否可以在不向相关实体的每个字符串数据集添加对子字符串的调用的情况下执行此操作?


答案 1

可以根据相应字段的 setter 中的 JPA 注释截断字符串:

public void setX(String x) {
    try {
        int size = getClass().getDeclaredField("x").getAnnotation(Column.class).length();
        int inLength = x.length();
        if (inLength>size)
        {
            x = x.substring(0, size);
        }
    } catch (NoSuchFieldException ex) {
    } catch (SecurityException ex) {
    }
    this.x = x;
}

注释本身应如下所示:

@Column(name = "x", length=100)
private String x;

(基于 https://stackoverflow.com/a/1946901/16673)

如果数据库发生更改,则可以从数据库重新创建批注,如注释中提示的那样,https://stackoverflow.com/a/7648243/16673


答案 2

你有不同的解决方案和错误的解决方案。

使用触发器或任何数据库级技巧

这将在 ORM 中的对象与 DB 中的序列化形式之间产生不一致。如果您使用二级缓存:它可能会导致很多麻烦。在我看来,这不是一般用例的真正解决方案。

使用预插入、预更新挂钩

在保留用户数据之前,您将以静默方式对其进行修改。因此,您的代码的行为可能会有所不同,具体取决于对象是否已持久化的事实。它也可能会引起麻烦。此外,您必须注意钩子调用的顺序:确保您的“字段截断器钩子”是持久性提供程序调用的第一个钩子。

使用 aop 拦截对 setter 的呼叫

此解决方案将或多或少地以静默方式修改用户/自动输入,但在使用对象执行某些业务逻辑后,不会对其进行修改。因此,这比之前的2个解决方案更容易接受,但是设置者不会遵循通常设置者的合同。此外,如果您使用字段注入:它将绕过该方面(根据您的配置,jpa提供程序可能会使用字段注入。大多数时候:弹簧使用设置器。我猜其他一些框架可能会使用字段注入,所以即使你没有明确使用它,也要注意你正在使用的框架的底层实现)。

使用 aop 截取字段修改

与前面的解决方案类似,除了现场注入也会被截取的方面。(请注意,我从来没有写过做这种事情的方面,但我认为这是可行的)

添加控制器层以在调用 setter 之前检查字段长度

就数据完整性而言,这可能是最好的解决方案。但它可能需要大量的重构。对于一般用例,这(在我看来)是唯一可接受的解决方案。

根据您的使用案例,您可以选择这些解决方案中的任何一个。注意缺点。


推荐