修剪JPA中的字符串字段
我有一个db表,其数据类型为char(20)。 我不允许将其更改为varchar。
我正在编写映射到此表的JPA实体。 我希望在我的实体类中表示此列的字符串字段始终包含修剪的值,而不是用db中存在的空格填充的20个字符的值。
我看不到任何简单的方法来做到这一点。 (注释会摇滚!)。 目前我只是从我的getter()返回一个修剪过的值,但这感觉就像一个kludge。
谷歌搜索没有提供任何帮助。 有任何想法吗?
或者您可以使用生命周期注释:
@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, Set> trimProperties = new HashMap, Set >(); @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 getTrimProperties(Class> entityClass) throws Exception { if (Object.class.equals(entityClass)) return Collections.emptySet(); Set propertiesToTrim = trimProperties.get(entityClass); if (propertiesToTrim == null) { propertiesToTrim = new HashSet (); 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字段, @Trim
监听器注册为persistence.xml中的默认实体监听器:
com.somepackage.TrimListener and.maybe.SomeOtherListener
接受的答案(使用JPA实体监听器/ @Trim注释)是一个危险的答案。 在检索到的实体上调用setter似乎将实体标记为脏。 当我在root实体级别(使用Spring3 / hibernate)自己尝试这个时,它会触发大量对相关实体的无关更新,否则这些实体在事务期间不会被修改。 这是一个真正的混乱的生产,并追溯到这是原因需要时间。
最后,我选择采用更简单的方法手动按需手动修剪每个字段(在自定义实体到域映射器中,或在实体getter中),类似于Edwin的答案。
您使用的JPA提供商是什么?
如果您正在使用EclipseLink,则默认情况下会修剪CHAR字段。 您可以通过会话trimStrings属性禁用它(确保您没有设置此属性)。
我正在使用这种方法,这使得修剪透明,而不必在每个字符串字段中使用注释。 在您拥有会话工厂类的同一个包中(用于获取Sessions的类,例如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中创建自定义注释,只要从数据库中获取对象,它就会“神奇地”修剪字符串。
将注释放在getter方法上,将@Acesss设置为AccessType.Property并使用String.trim()方法在那里修剪字段。
或者简单地将修剪放在getter方法中并始终通过它访问该字段。 它不会比那更简单。
如果您不介意使用纯Hibernate并偏离JPA标准,您可以使用Hibernate @ColumnTransformer,前提是您具有数据库function来完成工作
您可以在Hibernate参考中找到如何执行此操作:
我希望有所帮助!
所有你需要做的就是把它放在你的控制器上它按预期工作你不需要听众或其他任何东西
@InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); }
如果您的域要求声明它需要修剪信息,则需要将数据存储在修剪后的值中。 我认为没有错。
领域模型
包含行为和数据的域的对象模型。 (PEAA – Martin Fowler)
如果您明确必须在数据库级别强制执行业务规则,一个选项是您可以选择编写触发器,您可以使用内置的SQL trim方法。 但它就像使用火箭来破蛋。