Java 当字符串长度超过列长度定义时,如何在存储字符串时静默截断字符串?
我有一个web应用程序,使用EclipseLink和MySQL存储数据。 其中一些数据是字符串,即数据库中的varchar。 在实体代码中,字符串具有如下属性:Java 当字符串长度超过列长度定义时,如何在存储字符串时静默截断字符串?,java,jpa,eclipselink,Java,Jpa,Eclipselink,我有一个web应用程序,使用EclipseLink和MySQL存储数据。 其中一些数据是字符串,即数据库中的varchar。 在实体代码中,字符串具有如下属性: @Column(name = "MODEL", nullable = true, length = 256) private String model; @Entity(name = "PersonTable") public class MyEntity { @Convert(converter = Truncat
@Column(name = "MODEL", nullable = true, length = 256)
private String model;
@Entity(name = "PersonTable")
public class MyEntity {
@Convert(converter = TruncatedStringConverter.class)
private String veryLongValueThatNeedToBeTruncated;
//...
}
数据库不是由eclipseLink根据代码创建的,但长度与数据库中的varchar长度相匹配。
当此类字符串数据的长度大于length属性时,在调用javax.persistence.EntityTransaction.commit()期间会引发异常:
然后回滚事务。
虽然我知道这是默认行为,但这不是我想要的行为。
我希望数据被静默地截断,事务被提交
我可以在不向相关实体的每一组字符串数据方法添加子字符串调用的情况下执行此操作吗?我对EclipseLink一无所知,但是在Hibernate中是可行的-您可以使用实体元数据使org.Hibernate.Interceptor和onFlushDirty方法修改对象的当前状态。您可以为此使用描述符预插入/预更新事件,或者可能只使用JPA预插入和预更新事件 只需检查字段的大小并在事件代码中截断它们 如果需要,您可以从描述符映射的DatabaseField获取字段大小,或者使用Java反射从注释获取字段大小
在集合方法中进行截断可能会更好。那么你就不必担心事情了
您还可以在数据库上截断它,检查MySQL设置,或者可能使用触发器。还有另一种方法,速度可能更快(至少它在第5版MySQL上工作): 首先,检查您的sql\u模式设置:。对于windows,此设置的值应为“”,对于Unix,此设置的值应为“模式” 这对我没有帮助,所以我找到了另一个设置,这次是在jdbc中:
jdbcCompliantTruncation=false.
在我的例子中(我使用了persistence),它是在persistence.xml中定义的:
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/dbname?jdbcCompliantTruncation=false"/>
这两种设置只能一起工作,我试图单独使用它们,但没有效果
注意:请记住,通过如上所述设置sql_模式,您可以关闭重要的数据库检查,因此请小心操作。您可以将数据库设置为在非严格模式下工作,如下所述:
请注意,它也可能会取消其他验证,因此在您希望的情况下要小心,因为在commit()过程中似乎会在数据库级别引发异常,因此目标表上的before insert触发器可能会在提交新值之前截断新值,从而绕过错误。也许AOP会有帮助:
截取JavaBean/POJO中的所有set方法,然后获取要设置的字段。检查字段是否用
@列
注释,字段类型是否为字符串
。然后,如果字段的长度超过length
则截断该字段。如果希望逐个字段而不是全局执行此操作,则可以创建自定义类型映射,将值截断为给定长度,然后再将其插入表中。然后,可以通过注释将转换器附着到实体,如:
@Converter(name="myConverter", class="com.example.MyConverter")
并通过以下方式发送至相关字段:
@Convert("myConverter")
这实际上是为了支持自定义SQL类型,但也可能适用于普通的varchar类型字段。是关于制作这些转换器之一的教程。UI设计的两个非常重要的功能:
<input type="text" name="widgetDescription" maxlength="256">
这是一个系统限制-用户不能输入超过此限制的数据。如果这还不够,请更改数据库。可以根据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;
(基于)
如果数据库发生更改,则可以从数据库中重新创建注释,如对的注释所示,您有不同的解决方案和错误的解决方案 使用触发器或任何数据库级技巧 这将在ORM中的对象与其在DB中的序列化形式之间创建不一致性。如果你使用二级缓存:它会导致很多麻烦。在我看来,对于一般用例来说,这不是一个真正的解决方案 使用预插入、预更新挂钩 您将在持久化用户数据之前以静默方式修改它。因此,根据对象是否已持久化,代码的行为可能会有所不同。这也可能引起麻烦。此外,您必须注意钩子调用的顺序:确保您的“字段截断钩子”是持久性提供程序调用的第一个钩子 使用aop拦截对setter的调用 此解决方案或多或少会以静默方式修改用户/自动输入,但在使用对象执行某些业务逻辑后,不会修改对象。因此,这比之前的两种解决方案更容易接受,但setter不会遵循常规setter的约定。此外,如果您使用字段注入:它将绕过方面(取决于您的配置,jpa提供程序可能使用字段注入。大多数情况下:Spring使用setters。我猜其他一些框架可能使用字段注入,因此即使您不明确使用它,也要注意您正在使用的框架的底层实现) 使用aop拦截字段修改 类似于先前的解决方案exc
@Entity
public class MyEntity {
public static final int NAME_LENGTH=32;
private Long id;
private String name;
@Id @GeneratedValue
public Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
@Column(length=NAME_LENGTH)
public String getName() {
return name;
}
public void setName(String name) {
this.name = JpaUtil.truncate(name, NAME_LENGTH);
}
}
public class JpaUtil {
public static String truncate(String value, int length) {
return value != null && value.length() > length ? value.substring(0, length) : value;
}
}
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
@Convert
public class TruncatedStringConverter implements AttributeConverter<String, String> {
private static final int LIMIT = 999;
@Override
public String convertToDatabaseColumn(String attribute) {
if (attribute == null) {
return null;
} else if (attribute.length() > LIMIT) {
return attribute.substring(0, LIMIT);
} else {
return attribute;
}
}
@Override
public String convertToEntityAttribute(String dbData) {
return dbData;
}
}
@Entity(name = "PersonTable")
public class MyEntity {
@Convert(converter = TruncatedStringConverter.class)
private String veryLongValueThatNeedToBeTruncated;
//...
}