首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >春季EntityManager.persist问题

春季EntityManager.persist问题
EN

Stack Overflow用户
提问于 2018-11-25 01:16:52
回答 2查看 1.2K关注 0票数 0

我使用一个Spring框架(Boot+web+jpa)作为REST服务,并且需要持久化一个名为Sensor的实体。因为我使用Hibernate,所以我想继续使用EntityManager。从Spring文档和Stackoverflow中回答的一些问题中,我可以看到我应该配置LocalEntityManagerFactoryBeanLocalContainerEntityManagerFactoryBean

现在,我能够读取persist.xml,并通过依赖注入@PersistenceUnit(unitName="...") EntityManagerFactory来持久化实体,以防出现LocalContainerEntityManagerFactoryBean。但是,每次我尝试用new Sensor()持久化LocalEntityManagerFactoryBean时,Spring都会抛出一个错误,如下所示。

代码语言:javascript
复制
// LocalEntityManagerFactoryBean
@Bean
public LocalEntityManagerFactoryBean entityManagerFactory() {
    LocalEntityManagerFactoryBean em = new LocalEntityManagerFactoryBean();
    em.setPersistenceUnitName("myunit");
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(vendorAdapter);
    return em;
}

持久化(transaction.begin和end在调用方方法中):

代码语言:javascript
复制
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;
}

例外:

代码语言:javascript
复制
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

传感器类:

代码语言:javascript
复制
@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;
    }

}

有两个问题:

  1. 为什么会出错?
  2. 如果我不提供DataSource for LocalContainerEntityManagerFactoryBean,Spring会抛出一个错误?LocalEntityManagerFactoryBean使用来自persistence.xml的DataSource吐露为什么LocalContainerEntityManagerFactoryBean不做同样的操作?:

编辑1

我想我找到了解决办法。如果我从依赖项中删除spring devtools,它就解决了这个问题。事实上,当我使用spring devtools时,由于某种Sensor问题,我无法阅读ClassLoader。也许是什么虫子?:

代码语言:javascript
复制
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

代码语言:javascript
复制
@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 devtools将解决这个问题。我发现这与spring tools (https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html)中的重新启动类加载程序有关,每次重新启动spring时都会重新加载开发代码。但我认为文件的某些部分不会自动重新加载,从而导致类异常,例如,带有CL1 !=的实体加载了CL2。有人能解释一下我是不是做错了什么吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-02 02:30:06

由hibernate/Spring管理的实体类或Bean类的反序列化器似乎没有被spring devtools重新启动类加载器正确地重新加载。通过在META-INF/spring-devtools.properties中设置以下属性,我必须显式地包含对重新启动类加载器执行反序列化的类

代码语言:javascript
复制
restart.include.hibernate=hibernate.*
restart.include.spring-orm=spring-orm.*

资料来源:

https://github.com/AxonFramework/AxonFramework/issues/344

https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html#using-boot-devtools-customizing-classload

票数 1
EN

Stack Overflow用户

发布于 2018-11-25 04:25:30

需要更新访问修饰符私有而不是受保护,似乎Java持久性试图获取不在包中的id,在某些子类上也没有调用id。

代码语言:javascript
复制
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String deviceId;
private String name;

访问修饰符文档

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53463874

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档