如何在休眠状态下保留 AtomicInteger 而不是 Integer?
How to persist an AtomicInteger in hibernate instead of Integer?
我尝试使用 hibernate java 框架(保存后我需要在线程安全场景中使用该对象)但是当我尝试保存我的对象时 java 抛出:
java.lang.ClassCastException: java.util.concurrent.atomic.AtomicInteger cannot be cast to java.lang.Integer
有什么方法可以将 AtomicInteger 映射为整数吗?有一个对象的例子:
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>
还有休眠版本:
<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>
一种方法是编写一个 JPA 2.1 AttributeConverter 将 Integer 转换为 AtomicInteger,如下所示
@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);
}
}
通常您可以在 @Entity
class 字段上使用 JPA @Convert
注释,如下所示:
@Convert(converter = AtomicIntConverter.class)
private AtomicInteger totalErrors;
您可以在 Hibernate 5.2 documentation here.
中阅读更多相关信息
但是由于您使用的是休眠映射文件,请确保将转换器的 FQN class 设置为字段类型,而不是使用 @Converter
注释。
<property name="totalErrors" type="fully.qualified.name.of.AtomicIntConverter">
<column name="total_errors" />
</property>
最后更好的解决方案(因为是更标准的 hibernate 转换器 https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch06.html),是创建一个 hibernate UserType class。我不知道是什么原因,因为 AttributeConverter 不起作用(因为它在休眠文档中)。
这适用于我的 Hibernate 5.2。
创建一个实现休眠 UserType 的 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};
}
}
并在休眠映射文件中将 Integer 更改为 AtomicIntegerType,如下所示:
<?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>
我尝试使用 hibernate java 框架(保存后我需要在线程安全场景中使用该对象)但是当我尝试保存我的对象时 java 抛出:
java.lang.ClassCastException: java.util.concurrent.atomic.AtomicInteger cannot be cast to java.lang.Integer
有什么方法可以将 AtomicInteger 映射为整数吗?有一个对象的例子:
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>
还有休眠版本:
<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>
一种方法是编写一个 JPA 2.1 AttributeConverter 将 Integer 转换为 AtomicInteger,如下所示
@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);
}
}
通常您可以在 @Entity
class 字段上使用 JPA @Convert
注释,如下所示:
@Convert(converter = AtomicIntConverter.class)
private AtomicInteger totalErrors;
您可以在 Hibernate 5.2 documentation here.
中阅读更多相关信息但是由于您使用的是休眠映射文件,请确保将转换器的 FQN class 设置为字段类型,而不是使用 @Converter
注释。
<property name="totalErrors" type="fully.qualified.name.of.AtomicIntConverter">
<column name="total_errors" />
</property>
最后更好的解决方案(因为是更标准的 hibernate 转换器 https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch06.html),是创建一个 hibernate UserType class。我不知道是什么原因,因为 AttributeConverter 不起作用(因为它在休眠文档中)。
这适用于我的 Hibernate 5.2。 创建一个实现休眠 UserType 的 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};
}
}
并在休眠映射文件中将 Integer 更改为 AtomicIntegerType,如下所示:
<?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>