我正在将Spring框架(Boot + web + jpa)用于REST服务,并且需要持久保存名为Sensor
的实体。自从我使用Hibernate以来,我一直希望继续使用EntityManager
。从Spring Data Access文档以及Stackoverflow中回答的一些问题中,我可以看到我应该配置LocalEntityManagerFactoryBean
或LocalContainerEntityManagerFactoryBean
。
现在,我可以读取persist.xml
并通过依赖项注入@PersistenceUnit(unitName="...") EntityManagerFactory
持久保存实体,以防LocalContainerEntityManagerFactoryBean
出现。但是,每次尝试使用new Sensor()
持久化LocalEntityManagerFactoryBean
实例时,Spring都会不断抛出错误,如下所示。
// LocalEntityManagerFactoryBean
@Bean
public LocalEntityManagerFactoryBean entityManagerFactory() {
LocalEntityManagerFactoryBean em = new LocalEntityManagerFactoryBean();
em.setPersistenceUnitName("myunit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
持久化(transaction.begin和end在调用方方法中):
private Sensor getSensor(SimpleData sd) {
List<Sensor> sensors = entityManager.createQuery("select s from Sensor s where s.deviceId = :devid", Sensor.class)
.setParameter("devid", sd.getDeviceId())
.getResultList();
Sensor sensor = null;
if(sensors.isEmpty()) {
sensor = new Sensor();
sensor.setDeviceId(sd.getDeviceId());
sensor.setName(sd.getName());
entityManager.persist(sensor); // Throws an exception
deviceIdMapper.put(sensor.getDeviceId(), sensor.getId());
logger.trace("Cannot find the sensor in db, adding: "+sensor.getDeviceId());
} else {
sensor = sensors.get(0);
deviceIdMapper.put(sensor.getDeviceId(), sensor.getId());
logger.trace("Found a sensor from the db: "+sd.getDeviceId());
}
deviceIdMapper.putIfAbsent(sensor.getDeviceId(), sensor.getId());
logger.trace(sensor);
return sensor;
}
例外:
javax.persistence.PersistenceException: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [protected int com.database.Sensor.id] by reflection for persistent property [com.database.Sensor#id] : com.database.Sensor@6de9600a
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:807)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:785)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350)
at com.sun.proxy.$Proxy108.persist(Unknown Source)
at com.DataListener.getSensor(DataListener.java:69)
at com.DataListener.lambda$0(DataListener.java:87)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at com.DataListener.processData(DataListener.java:86)
at com.DataListener.onMessage(DataListener.java:49)
at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1321)
at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131)
at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202)
at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:129)
at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [protected int com.database.Sensor.id] by reflection for persistent property [com.database.Sensor#id] : com.database.Sensor@6de9600a
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:75)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:224)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4931)
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:4631)
at org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:226)
at org.hibernate.event.internal.AbstractSaveEventListener.getEntityState(AbstractSaveEventListener.java:540)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:62)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:800)
... 20 more
Caused by: java.lang.IllegalArgumentException: Can not set int field com.database.Sensor.id to com.database.Sensor
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at java.base/jdk.internal.reflect.UnsafeIntegerFieldAccessorImpl.getInt(UnsafeIntegerFieldAccessorImpl.java:56)
at java.base/java.lang.reflect.Field.getInt(Field.java:594)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:62)
... 28 more
传感器类别:
@Entity
public class Sensor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected int id;
protected String deviceId;
protected String name;
@OneToMany(mappedBy = "owner", cascade=CascadeType.ALL)
private List<SimpleData> simpleDevices = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<SimpleData> getSimpleDevices() {
return simpleDevices;
}
public void setSimpleDevices(List<SimpleData> simpleDevices) {
this.simpleDevices = simpleDevices;
}
}
有两个问题:
DataSource
提供LocalContainerEntityManagerFactoryBean
,Spring会引发错误? LocalEntityManagerFactoryBean
使用来自persistence.xml
的DataSource配置,为什么LocalContainerEntityManagerFactoryBean
不这样做?:编辑1
我想我已经找到了解决方案。如果我从依赖项中删除spring-boot-devtools,它将解决此问题。实际上,当我使用spring-boot-devtools时,由于某种ClassLoader问题,我无法读取Sensor
。也许是某种错误?:
java.lang.ClassCastException: class com.database.Sensor cannot be cast to class com.database.Sensor (com.database.Sensor is in unnamed module of loader 'app'; com.database.Sensor is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @6228d409)
编辑2
这是SimpleData
类
@Entity
public class SimpleData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private double value;
@JsonIgnore
private Date time;
@Transient
private String deviceId;
@JsonIgnore
@Transient
private String name;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="sensor_id")
private Sensor owner;
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public Sensor getOwner() {
return owner;
}
public void setOwner(Sensor owner) {
this.owner = owner;
}
}
编辑3
好的,我制作了一个小型测试程序来重现该问题:https://github.com/hjparker/spring-boot-devtools-issue。
服务器启动后,您可以向localhost:8080发出GET请求,这将引发错误。在build.gradle中注释掉spring-boot-devtools可以解决这个问题。我发现这与spring-dev-tools(https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html)中的重新启动类加载器有关,该类加载器在每次重新启动spring时都重新加载开发代码。但是我认为文件的某些部分不会自动重新加载,从而导致classcastexception,例如装有CL1的实体!=装有CL2的实体。有人可以解释我做错了什么吗?
答案 0 :(得分:1)
似乎由spring / boot-devtools restart classloader未正确加载由hibernate / Spring ORM管理的Entity或Bean类的反序列化器。我必须通过在META-INF/spring-devtools.properties
中设置以下属性,来显式地包括对重新启动类加载器执行反序列化的类:
restart.include.hibernate=hibernate.*
restart.include.spring-orm=spring-orm.*
来源:
答案 1 :(得分:0)
需要更新访问修饰符私有而不是受保护的修饰符,似乎Java持久性试图获取ID,该ID不在包中,也不在某些子类上调用。
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String deviceId;
private String name;