我有一个db表,其数据类型为char(20)。我不允许将其更改为varchar。
我正在编写映射到此表的JPA实体。我希望在我的实体类中表示此列的字符串字段始终包含修剪后的值,而不是填充db中存在的空格的20个字符值。
我看不到任何简单的方法。 (注释会晃动!)。目前我只是从我的getter()返回一个修剪过的值,但这感觉就像一个kludge。
谷歌搜索没有提供任何帮助。有什么想法吗?
答案 0 :(得分:24)
或者您可以使用生命周期注释:
@Entity
public class MyEntity {
@PostLoad
protected void repair(){
if(myStringProperty!=null)myStringProperty=myStringProperty.trim();
}
private String myStringProperty;
public String getMyStringProperty() {
return myStringProperty;
}
public void setMyStringProperty(String myStringProperty) {
this.myStringProperty = myStringProperty;
}
}
如果在多个实体上发生这种情况,您可以创建自定义注释并编写专用的EntityListener。
<强>注释强>
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Trim {}
<强>监听强>
public class TrimListener {
private final Map<Class<?>, Set<Field>> trimProperties =
new HashMap<Class<?>, Set<Field>>();
@PostLoad
public void repairAfterLoad(final Object entity) throws Exception {
for (final Field fieldToTrim : getTrimProperties(entity.getClass())) {
final String propertyValue = (String) fieldToTrim.get(entity);
if (propertyValue != null)
fieldToTrim.set(entity, propertyValue.trim());
}
}
private Set<Field> getTrimProperties(Class<?> entityClass) throws Exception {
if (Object.class.equals(entityClass))
return Collections.emptySet();
Set<Field> propertiesToTrim = trimProperties.get(entityClass);
if (propertiesToTrim == null) {
propertiesToTrim = new HashSet<Field>();
for (final Field field : entityClass.getDeclaredFields()) {
if (field.getType().equals(String.class)
&& field.getAnnotation(Trim.class) != null) {
field.setAccessible(true);
propertiesToTrim.add(field);
}
}
trimProperties.put(entityClass, propertiesToTrim);
}
return propertiesToTrim;
}
}
现在使用@Trim
注释所有相关的String字段,并将监听器注册为persistence.xml中的默认实体监听器:
<persistence-unit ..>
<!-- ... -->
<default-entity-listeners>
com.somepackage.TrimListener
and.maybe.SomeOtherListener
</default-entity-listeners>
</persistence-unit>
答案 1 :(得分:16)
接受的答案(使用JPA实体侦听器/ @Trim注释)是危险的。在检索到的实体上调用setter似乎将实体标记为脏。当我在root实体级别(使用Spring3 / hibernate)自己尝试这个时,它会触发大量对相关实体的无关更新,否则这些更新在事务期间不会被修改。这是一个真正的混乱的生产,并追溯到这是原因需要时间。
最后,我选择采用更直接的方法手动按需手动修剪每个字段(在自定义实体到域映射器中,或在实体getter中),类似于Edwin的答案。
答案 2 :(得分:5)
这是一个古老的问题,但对我来说很有用。就我而言,最简单的方法是创建一个简单的“ javax.persistence.Converter”,如下所示:
@Converter
public class StringTrimConverter implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
return attribute;
}
@Override
public String convertToEntityAttribute(String dbData) {
return dbData.trim();
}
}
您可以像这样使用它:
@Entity
@Table(name = "ViewAddress")
public class PostalAddress extends DbObject {
@Convert(converter = StringTrimConverter.class)
private String street;
@Convert(converter = StringTrimConverter.class)
private String number;
(...)
效果很好。
答案 3 :(得分:2)
您使用的是哪个JPA提供商?
如果您正在使用EclipseLink,则默认情况下会修剪CHAR字段。您可以通过会话trimStrings属性禁用它(确保您没有设置它)。
答案 4 :(得分:2)
我正在使用这种方法,这使得修剪透明,而不必在每个字符串字段中使用注释。
在您拥有会话工厂类的同一个包中(用于获取会话的会话工具类,例如org.blablabla.yourpackage.etc.SessionGetter.getSession()
),您必须创建一个名为package-info.java
的文件并将此内容放入其中:
@TypeDefs({
@TypeDef(name = "trimmedStringType",
defaultForType = String.class,
typeClass = StringUserType.class)
})
package org.blablabla.yourpackage.etc;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
然后在同一个包中创建类StringUserType
:
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.usertype.EnhancedUserType;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
public class StringUserType implements EnhancedUserType, Serializable {
private static final int[] SQL_TYPES = new int[]{Types.VARCHAR};
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public Class returnedClass() {
return String.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if (x == null || y == null) {
return false;
}
String dtx = (String) x;
String dty = (String) y;
return dtx.equals(dty);
}
@Override
public int hashCode(Object object) throws HibernateException {
return object.hashCode();
}
@Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Object s = StandardBasicTypes.STRING.nullSafeGet(resultSet, names, session, owner);
if (s == null) {
return null;
}
return s.toString().trim();
}
@Override
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (value == null) {
StandardBasicTypes.STRING.nullSafeSet(preparedStatement, null, index, session);
} else {
StandardBasicTypes.STRING.nullSafeSet(preparedStatement, value.toString().trim(), index, session);
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
@Override
public Object assemble(Serializable cached, Object value) throws HibernateException {
return cached;
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
@Override
public String objectToSQLString(Object object) {
throw new UnsupportedOperationException();
}
@Override
public String toXMLString(Object object) {
return object.toString();
}
@Override
public Object fromXMLString(String string) {
return string;
}
}
就是这样,不需要在bean中创建自定义注释,只要从数据库中获取对象,它就会“神奇地”修剪字符串。
答案 5 :(得分:2)
可接受的答案有效,除了在persistence.xml中注册侦听器外。我是用orm.xml完成的。
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"
version="2.1">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class="org.example.TrimListener">
</entity-listener>
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
答案 6 :(得分:1)
将注释放在getter方法上,将@Acesss设置为AccessType.Property并使用String.trim()方法在那里修剪字段。
或者简单地将修剪放在getter方法中并始终通过它访问该字段。它不会比那更简单。
如果您不介意使用纯Hibernate并偏离JPA标准,您可以使用Hibernate @ColumnTransformer,前提是您拥有数据库功能来完成
的工作您可以在Hibernate参考中找到如何执行此操作:
我希望有所帮助!
答案 7 :(得分:1)
你需要做的就是将它放在你的控制器上,它按预期运行你不需要听众或其他任何东西
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
答案 8 :(得分:0)
如果您的域名要求声明需要修剪信息,则需要将数据存储在修剪后的值中。我认为没有错。
域名模型
一个对象模型 包含两种行为的域 和数据。 (PEAA - Martin Fowler)
如果您明确必须在数据库级别强制执行业务规则,一个选项是您可以选择编写触发器,您可以使用内置的SQL trim方法。但这就像使用火箭来破蛋一样。