Может ли JPA AttributeConverter узнать что-либо о том, на чем работает Entity? - PullRequest
2 голосов
/ 28 февраля 2020

Интерфейс для AttributeConverter выглядит следующим образом:

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

В моей реализации я хотел бы немного узнать о сущности и поле сущности, в которой конвертер работает на. Например: должен ли этот конвертер дешифровать это поле для приложения или нет?

JAX-RS имеет концепцию @Context, что довольно удобно. Любопытно, если JPA имеет эквивалентную концепцию.

Ответы [ 2 ]

1 голос
/ 29 февраля 2020

Вы, вероятно, могли бы использовать слушатель сущности для этой цели:

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 вы также сможете выполнять внедрение зависимостей в прослушивателе сущностей.

1 голос
/ 29 февраля 2020

Я не думаю, что вы можете сделать это с простыми конвертерами JPA (см. это ). Но вы можете сделать это с помощью Hibernate пользовательских типов , которые реализуют интерфейс 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;
}
...