【问题标题】:Is it possible for a JPA AttributeConverter to know anything about what Entity is running on?JPA AttributeConverter 是否有可能知道有关 Entity 正在运行的任何信息?
【发布时间】:2020-06-12 20:51:58
【问题描述】:

AttributeConverter的接口如下:

public interface AttributeConverter<X,Y> {
    public Y convertToDatabaseColumn (X attribute);
    public X convertToEntityAttribute (Y dbData);
}

在我的实现中,我想了解一些有关转换器运行的实体和实体的字段。例如:这个转换器是否应该为应用程序解密这个字段?

JAX-RS 有@Context 的概念,非常方便。好奇 JPA 是否有等效的概念。

【问题讨论】:

    标签: java hibernate jpa eclipselink


    【解决方案1】:

    我认为你不能用普通的 JPA 转换器来做到这一点(参见 this)。但是您可以使用 Hibernate custom types 来实现,它实现了 DynamicParameterizedType 接口。

    还有一个简单的例子:

    import java.io.Serializable;
    import java.lang.annotation.Annotation;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Timestamp;
    import java.sql.Types;
    import java.time.LocalDateTime;
    import java.util.Objects;
    import java.util.Properties;
    
    import javax.persistence.Column;
    
    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SharedSessionContractImplementor;
    import org.hibernate.usertype.DynamicParameterizedType;
    import org.hibernate.usertype.UserType;
    
    public class MyConverter implements UserType, DynamicParameterizedType
    {
       private int sqlType;
       private Object entity; // com.example.hibernate.Account
       private int columnLength;
    
       public MyConverter()
       {
       }
    
       @Override
       public void setParameterValues(Properties parameters)
       {
          sqlType = getSqlType(parameters.get(RETURNED_CLASS));
    
          Object entity = parameters.get(ENTITY);
          ParameterType reader = (ParameterType) parameters.get(PARAMETER_TYPE);
          this.columnLength = getLength(reader);
       }
    
       private int getSqlType(Object field)
       {
          if (field instanceof Long) return Types.BIGINT;
          if (field instanceof LocalDateTime) return Types.TIMESTAMP;
          return Types.VARCHAR;
       }
    
       private int getLength(ParameterType reader)
       {
          int length = -1; // default length
          for (Annotation annotation : reader.getAnnotationsMethod()){
             if (annotation instanceof Column) {
                length =  ((Column) annotation).length();
             }
          }
          return length;
       }
    
       @Override
       public int[] sqlTypes()
       {
          return new int[] {sqlType};
       }
    
       @Override
       public Class<?> returnedClass()
       {
          return String.class;
       }
    
       @Override
       public boolean equals(Object x, Object y) throws HibernateException
       {
          return Objects.equals(x, y);
       }
    
       @Override
       public int hashCode(Object x) throws HibernateException
       {
          return Objects.hashCode(x);
       }
    
       @Override
       public Object nullSafeGet(ResultSet rs,
             String[] names,
             SharedSessionContractImplementor session,
             Object owner) throws HibernateException, SQLException 
       {
          Object result = rs.getObject(names[0]);
    
          if (result instanceof Timestamp) {
             return ((Timestamp) result).toLocalDateTime();
          }
    
          return result;
       }
    
       @Override
       public void nullSafeSet(PreparedStatement st,
             Object value,
             int index,
             SharedSessionContractImplementor session) throws HibernateException, SQLException
       {
          if (value == null) {
             st.setNull(index, sqlType);
          }
    
          if (value instanceof LocalDateTime) {
             st.setTimestamp(index, Timestamp.valueOf((LocalDateTime) value));
          }
    
          if (value instanceof Long) {
             st.setLong(index, (Long) value);
          }
    
          if (value instanceof String) {
            st.setString(index, (String)value);
          }
       }
    
       @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 Objects.toString(value);
       }
    
       @Override
       public Object assemble(Serializable cached, Object owner) throws HibernateException
       {
          return cached;
       }
    
       @Override
       public Object replace(Object original, Object target, Object owner) throws HibernateException
       {
          return original;
       }
    }
    
    

    及用法:

    @Entity
    public class Account
    {
       @Id
       @Type(type = "com.example.hibernate.MyConverter")
       @Column(name = "acc_id")
       private Long id;
    
       @Column(name = "acc_name", length = 50)
       @Type(type = "com.example.hibernate.MyConverter")
       private String name;
    
       @Column(name = "acc_regdt")
       @Type(type = "com.example.hibernate.MyConverter")
       private LocalDateTime regDate;
    }
    

    【讨论】:

      【解决方案2】:

      您可能会为此目的使用实体侦听器:

      public class FooEntityListener {
      
          @PreUpdate
          public void encrypt(Foo foo) {
              ...
          }
      
          @PostLoad
          public void decrypt(Foo foo) {
              ...
          }
      }
      
      @Entity
      @EntityListeners(class = FooEntityListener.class)
      public class Foo {
          ...
      }
      

      而且,如果你使用 Spring,你可以在实体监听器中执行依赖注入:

      @Component
      public class FooEntityListener {
      
          private SomeSortOfSpringBean bean;
      
          @Autowired
          public FooEntityListener(SomeSortOfSpringBean bean){
              this.bean = bean;
          }
      
          @PreUpdate
          public void encrypt(Foo foo) {
              ...
          }
      }
      

      我没有测试过,但是使用 CDI 和 JPA 2.1,你应该也可以在实体监听器中执行依赖注入。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-08
        • 2013-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多