代码之家  ›  专栏  ›  技术社区  ›  Martin

如何在hibernate中持久化原子整数而不是整数?

  •  0
  • Martin  · 技术社区  · 8 年前

    我试图持久化具有 原子整数 使用 java framework(保存对象后,我需要在线程安全场景中使用该对象),但当我尝试保存对象时,java抛出:

    java.lang.ClassCastException: java.util.concurrent.atomic.AtomicInteger cannot be cast to java.lang.Integer
    

    public class Statistics implements java.io.Serializable {
      private AtomicInteger id;
      private AtomicInteger totalErrors;
    
      public Statistics() {
      }
    
    
      public AtomicInteger getTotalErrors() {
        return this.totalErrors;
      }
    
      public void seTotalErrors(AtomicInteger totalErrors) {
        this.totalErrors= totalErrors;
      }
    }
    

    和相应的POJO xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
       <class name="Statistics" table="statistics" catalog="example" optimistic-lock="version">
          <id name="id" type="java.lang.Integer">
             <column name="id" />
             <generator class="identity" />
          </id>
          <property name="totalErrors" type="java.lang.Integer">
             <column name="total_errors" />
          </property>
       </class>
    </hibernate-mapping>
    

    还有hibernate版本:

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.10.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-hikaricp</artifactId>
            <version>5.2.10.Final</version>
        </dependency>
    
    2 回复  |  直到 8 年前
        1
  •  3
  •   Nico Van Belle    8 年前

    一种方法是编写JPA 2.1 AttributeConverter 从整数转换为原子整数,如下所示

    @Converter
    public class AtomicIntConverter implements AttributeConverter<AtomicInteger, Integer> {
    
    
     @Override
     public Integer convertToDatabaseColumn(AtomicInteger attribute) {
      return attribute.get();
     }
    
     @Override
     public AtomicInteger convertToEntityAttribute(Integer dbData) {
      return new AtomicInteger(dbData);
     }
    
    }
    

    通常,您可以使用JPA @Convert 您的 @Entity 类字段如下:

    @Convert(converter = AtomicIntConverter.class)
    private AtomicInteger totalErrors;
    

    Hibernate 5.2 documentation here.

    但是,在使用hibernate映射文件时,请确保将转换器类的FQN设置为字段类型,而不是使用 @Converter

    <property name="totalErrors" type="fully.qualified.name.of.AtomicIntConverter">
         <column name="total_errors" />
     </property>
    
        2
  •  0
  •   Martin    8 年前

    最后是更好的解决方案(因为是更标准的hibernate转换器 https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch06.html

    创建实现hibernate用户类型的AtomicIntegerType:

    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.logging.Logger;
    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SharedSessionContractImplementor;
    import org.hibernate.usertype.UserType;
    
    public class AtomicIntegerType implements UserType {
    
        private static final Logger logger = Logger.getLogger("AtomicInteger");
    
        /**
         * Returns the object from the 2 level cache
         */
        @Override
        public Object assemble(final Serializable cached, final Object owner)
                throws HibernateException {
            //would work as the AtomicInteger.class is Serializable, 
            //and stored in cache as it is - see disassemble
            return cached;
        }
    
        /**
         * Used to create Snapshots of the object
         */
        @Override
        public Object deepCopy(Object value) throws HibernateException {
            //return value; -> if AtomicInteger.class was immutable we could return the object as it is
            final AtomicInteger recievedParam = (AtomicInteger) value;
            final AtomicInteger atomicInteger = new AtomicInteger(recievedParam.get());
            return atomicInteger;
        }
    
        /**
         * method called when Hibernate puts the data in a second level cache. The
         * data is stored in a serializable form
         */
        @Override
        public Serializable disassemble(final Object value) throws HibernateException {
            //For this purpose the AtomicInteger.class must implement serializable
            return (Serializable) value;
        }
    
        /**
         * Used while dirty checking - control passed on to the
         * {@link AtomicInteger}
         */
        @Override
        public boolean equals(final Object o1, final Object o2) throws HibernateException {
            boolean isEqual = false;
            if (o1 == o2) {
                isEqual = true;
            }
            if (null == o1 || null == o2) {
                isEqual = false;
            } else {
                isEqual = o1.equals(o2);
            }
            return isEqual;
            //for this to work correctly the equals() 
            //method must be implemented correctly by AtomicInteger class
        }
    
        @Override
        public int hashCode(final Object value) throws HibernateException {
            return value.hashCode();
            //for this to work correctly the hashCode() 
            //method must be implemented correctly by AtomicInteger class
    
        }
    
        /**
         * Helps hibernate apply certain optimizations for immutable objects
         */
        @Override
        public boolean isMutable() {
            return true; //The audit fields can be modified
        }
    
        /**
         * This method retrieves the property value from the JDBC resultSet
         */
        @Override
        public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor ssci, Object owner) throws HibernateException, SQLException {
            //owner here is class from where the call to retrieve data was made.
            //In this case the Test class
            AtomicInteger atomicInteger = null;
            if (!resultSet.wasNull()) {
                atomicInteger = new AtomicInteger(resultSet.getInt(names[0]));            
            }
            return atomicInteger;
        }
    
        /**
         * The method writes the property value to the JDBC prepared Statement
         *
         */
        @Override
        public void nullSafeSet(final PreparedStatement statement,
                final Object value, final int index, SharedSessionContractImplementor ssci) throws HibernateException,
                SQLException {
            if (null == value) {
                statement.setNull(index, java.sql.Types.INTEGER);
            } else {
                AtomicInteger atomicInteger = (AtomicInteger) value;
                if (null != atomicInteger) {
                    statement.setInt(index , atomicInteger.get());
                } else {
                    statement.setNull(index, java.sql.Types.INTEGER);
                }
    
            }
        }
    
        /**
         * Method used by Hibernate to handle merging of detached object.
         */
        @Override
        public Object replace(final Object original, final Object target,
                final Object owner)
                throws HibernateException {
            //return original; // if immutable use this
            //For mutable types at bare minimum return a deep copy of first argument
            return this.deepCopy(original);
    
        }
    
        /**
         * Method tells Hibernate which Java class is mapped to this Hibernate Type
         */
        @SuppressWarnings("rawtypes")
        @Override
        public Class returnedClass() {
            return AtomicInteger.class;
        }
    
        /**
         * Method tells Hibernate what SQL columns to use for DDL schema generation.
         * using the Hibernate Types leaves Hibernate free to choose actual SQl
         * types based on database dialect. (Alternatively SQL types can also be
         * used directly)
         */
        @Override
        public int[] sqlTypes() {
            //createdBy, createdDate,modifiedBy,modifiedDate
            return new int[]{java.sql.Types.INTEGER};
        }
    
    }
    

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <!-- Generated 12-sep-2017 13:14:50 by Hibernate Tools 4.3.1 -->
    <hibernate-mapping>
       <class name="classs.location.Statistics" table="statistics" catalog="CATALOG" optimistic-lock="version">
          <id name="id" type="java.lang.Integer">
             <column name="id" />
             <generator class="identity" />
          </id>
          <property name="totalErrors" type="class.location.AtomicIntegerType">
             <column name="total_errors" />
          </property>
       </class>
    </hibernate-mapping>