Spring EntityManager.persist issue - java

I am using a Spring framework (Boot+web+jpa) for a REST service and needs to persist an Entity called Sensor. Since I was using Hibernate, I wanted to keep usingEntityManager. From the Spring Data Access document and some of the questions answered in Stackoverflow, I could see that I should configure LocalEntityManagerFactoryBean or LocalContainerEntityManagerFactoryBean.
Now I was able to read persist.xml and persist the entity through dependency injection #PersistenceUnit(unitName="...") EntityManagerFactory in case for LocalContainerEntityManagerFactoryBean. However, Spring keeps throwing an error everytime when I try to persist new Sensor() instance with LocalEntityManagerFactoryBean as shown below.
// LocalEntityManagerFactoryBean
#Bean
public LocalEntityManagerFactoryBean entityManagerFactory() {
LocalEntityManagerFactoryBean em = new LocalEntityManagerFactoryBean();
em.setPersistenceUnitName("myunit");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
Persisting (transaction.begin and end are in the caller method):
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;
}
Exception:
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
Sensor class:
#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;
}
}
There are two questions:
Why the error?
Spring throws an error if I do not provide DataSource for LocalContainerEntityManagerFactoryBean? LocalEntityManagerFactoryBean uses DataSource configs from persistence.xml Why LocalContainerEntityManagerFactoryBean doesn't do the same?:
Edit 1
I think I have found a solution. If I remove spring-boot-devtools from the dependency, it fixes the problem. In fact when I use spring-boot-devtools I could not read Sensor due to some sort of ClassLoader problem. Maybe some sort of a bug?:
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)
Edit 2
Here is SimpleData class
#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;
}
}
Edit 3
Okay, I made a small test program that reproduces the problem: https://github.com/hjparker/spring-boot-devtools-issue.
Once the server is up, you can make a GET request to localhost:8080, which will throw an error. Commenting out spring-boot-devtools in build.gradle will solve the problem. I found this is something to do with the restart classloader in spring-dev-tools (https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html), which reloads developing code every time spring is restarted. But I think some part of the file is not reloaded automatically causing the classcastexception, e.g. An entity loaded with CL1 != An entity loaded with CL2. Can someone explain if I am doing something wrong?

It seems some of the deserializers for Entity or Bean classes managed by hibernate/Spring ORM are not reloaded by the spring-boot-devtools restart classloader properly. I had to explicitly include the classes that perform deserialization to the restart classloader by setting the following properties in META-INF/spring-devtools.properties:
restart.include.hibernate=hibernate.*
restart.include.spring-orm=spring-orm.*
Sources:
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

Need to update access modifier private instead of protected, seems that Java persistence tries to get id which is not in package neither is called on some subclass.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String deviceId;
private String name;
Access modifier documentation

Related

Hibernate: Program run persists new created entities together with entities which were persisted in previous program run and which I had deleted

This is maybe a beginner question on hibernate. I am doing my first steps, I designed a simple datamodel consisting of about 10 entities and I use hibernate to persist them to my Oracle XE database. Now I am facing the following problem: First time, when I do a transaction to persist some entities, they are persisted properly. I verify, that the data exists in the database and then I delete all the entries from all database tables. I verify that all tables are empty again. Then I run my program again to persist some new entities - and here happens something really strange: Afterwards I find in my databse the new entries as well as the old ones, which were persisted last time and which I had deleted! They contained the old IDs and the old data fields! How can this be? This happens even if I shut down my computer after the first time the program runs! How does it remember the old entries and where are they saved? Do you have any ideas?
Some information, that might be useful:
I am using annotations (instead of config files) for the mapping.
Following you see the classes used for persisting as well as one example of an entity (I am showing only one entity to avoid making the question too long).
As you see, I am using FetchType = EAGER on my MANY to MANY mappings (as I understand, this makes sure, that all related entities are loaded immediately together with any loaded entity). Can this have any impact?
Thanks for any help!
public class PersistenceManager {
private static final SessionFactory factory = new Configuration().configure().buildSessionFactory();
public static void sampleData() {
try(Session session = factory.openSession()) {
SampleDataLoader.loadSampleData(session);
} catch(HibernateException e) {
System.out.println("Exception during persisting! Message: " + e.getMessage());
e.printStackTrace();
}
}
}
public class SampleDataLoader {
static void loadSampleData(Session session) {
Language french = new Language("French");
Language german = new Language("German");
Noun garcon = new Noun(french, "garcon", false);
Noun junge = new Noun(german, "Junge", false);
junge.addTranslation(garcon);
ZUser user = new ZUser("Daniel", "password");
user.setOwnLanguage(german);
user.setEmail("abc#somemail.de");
user.setDateRegistered(LocalDateTime.now());
user.addForeignLanguage(french);
Transaction transaction = session.beginTransaction();
session.save(user);
session.save(french);
session.save(german);
session.save(junge);
transaction.commit();
}
}
#Entity
public class ZUser {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column
private String name;
#Column
private String password;
#Column
private String email;
#Column
private String picturePath;
#Column
private LocalDateTime dateRegistered;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="OWNLANGUAGE_ID")
private Language ownLanguage;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(name="USER_LANGUAGE",
joinColumns=#JoinColumn(name="USER_ID"),
inverseJoinColumns=#JoinColumn(name="LANGUAGE_ID")
)
private Set<Language> foreignLanguages = new HashSet<>();
public ZUser() { }
public ZUser(String n, String p) {
name = n;
password = p;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPicturePath() { return picturePath; }
public void setPicturePath(String picturePath) { this.picturePath = picturePath; }
public LocalDateTime getDateRegistered() { return dateRegistered; }
public void setDateRegistered(LocalDateTime dateRegistered) { this.dateRegistered = dateRegistered; }
public Language getOwnLanguage() { return ownLanguage; }
public void setOwnLanguage(Language ownLanguage) { this.ownLanguage = ownLanguage; }
public void addForeignLanguage(Language language) {foreignLanguages.add(language);}
public Set<Language> getForeignLanguages() {return Collections.unmodifiableSet(foreignLanguages); }
}
Clarified by the comment of Jagger (see comments). Indeed, I was using Oracle SQL command line to delete the entries and I had rgotten, that I need to explicitely commit after deleting. The solution can be so easy :)

ORM in Morphia for a Nested Class

My Json document in the Morphia DB looks like this -
{
"_id" : ObjectId("58fcdf7e"),
"status" : "ACTIVE",
"user" : {
"id" : NumberLong(228),
"email" : "testing#domian.com"
}
}
I have created a Java class for this collection which looks like this -
#Entity("member_offer")
public class MemberOffer {
#Id
private ObjectId objectId;
#Property("status")
private String status;
#Embedded("user")
private UserDetail user;
#Embedded
class UserDetail {
#Property("id")
public long memberId;
#Property("email")
public String email;
UserDetail() {
}
}
public ObjectId getObjectId() {
return objectId;
}
public void setObjectId(ObjectId objectId) {
this.objectId = objectId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public UserDetail getUser() {
return user;
}
public void setUser(UserDetail user) {
this.user = user;
}
}
Now when I am trying to fetch the data I am getting this exception -
java.lang.RuntimeException:
org.mongodb.morphia.mapping.MappingException: No usable constructor
for vo.MemberSubscription$UserDetail
Caused by: org.mongodb.morphia.mapping.MappingException: No usable
constructor for vo.MemberSubscription$UserDetail
Caused by: org.mongodb.morphia.mapping.MappingException: No usable constructor for vo.MemberSubscription$UserDetail
Caused by: java.lang.NoSuchMethodException: vo.MemberSubscription$UserDetail.()
Any idea how I can resolve this issue? I want UserDetail to be nested class only, I know if I create it as an independent class this error can be resolved. But my question here is can something like this (having nested class) can be achieved in Morphia?
Also if there is some fundamental flaw in my design please educate me about it.
You should try to use public modifier for the constructor, also make UserDetail (inner class) is static.

Caused by: org.springframework.data.mapping.model.MappingException: No property territoryID found on com.common.model.EmployeeTerritory! - Spring Boot

I am developing Spring Boot MongoDB example. In this example, I am trying to get TerritoryID by spring-data-mongo repository query and facing below error. Please guide
Caused by: org.springframework.data.mapping.model.MappingException: No property territoryID found on com.common.model.EmployeeTerritory!
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentPropertyPath(AbstractMappingContext.java:236) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentPropertyPath(AbstractMappingContext.java:214) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentPropertyPath(AbstractMappingContext.java:202) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.query.MongoQueryCreator.create(MongoQueryCreator.java:109) ~[spring-data-mongodb-1.6.3.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.query.MongoQueryCreator.create(MongoQueryCreator.java:55) ~[spring-data-mongodb-1.6.3.RELEASE.jar:na]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.query.PartTreeMongoQuery.createQuery(PartTreeMongoQuery.java:75) ~[spring-data-mongodb-1.6.3.RELEASE.jar:na]
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:86) ~[spring-data-mongodb-1.6.3.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:415) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:393) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:506) ~[spring-data-commons-1.9.3.RELEASE.jar:na]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.1.7.RELEASE.jar:4.1.7.RELEASE]
at com.sun.proxy.$Proxy35.findByTerritoryID(Unknown Source) ~[na:na]
at com.common.service.EmployeeTerritoryServiceImpl.findByTerritoryID(EmployeeTerritoryServiceImpl.java:18) ~[classes/:na]
at com.common.main.MainController.run(MainController.java:51) ~[classes/:na]
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:672) [spring-boot-1.2.5.RELEASE.jar:1.2.5.RELEASE]
... 5 common frames omitted
16-03-22 22:33:38 [main] INFO o.s.c.a.AnnotationConfigApplicationContext - Closing org.springframework.context.annotation.AnnotationConfigApplicationContext#576d5deb: startup date [Tue Mar 22 22:33:26 IST 2016]; root of context hierarchy
16-03-22 22:33:38 [main] INFO o.s.j.e.a.AnnotationMBeanExporter - Unregistering JMX-exposed beans on shutdown
Exception in thread "main" java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:675)
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:690)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.common.main.Main.main(Main.java:17)
The code I developed:
EmployeeTerritory.java
#Document(collection="employee-territories")
public class EmployeeTerritory {
#Id
private ObjectId id;
#Field
private String EmployeeID;
#Field(value="TerritoryID")
private Integer TerritoryID;
// setters and getters
}
EmployeeTerritoryRepository.java
public interface EmployeeTerritoryRepository extends CrudRepository<EmployeeTerritory, String>{
EmployeeTerritory findByTerritoryID(Integer territoryID);
}
EmployeeTerritoryService.java
public interface EmployeeTerritoryService {
EmployeeTerritory findByTerritoryID(Integer territoryID);
}
EmployeeTerritoryServiceImpl.java
#Component
public class EmployeeTerritoryServiceImpl implements EmployeeTerritoryService{
#Autowired
private EmployeeTerritoryRepository employeeTerritoryRepository;
#Override
public EmployeeTerritory findByTerritoryID(Integer territoryID) {
EmployeeTerritory et = employeeTerritoryRepository.findByTerritoryID(territoryID);
return et;
}
}
MainController.java
#Controller
public class MainController implements CommandLineRunner {
private Logger LOGGER = Logger.getLogger(MainController.class);
#Autowired
private EmployeeTerritoryService etService;
#Override
public void run(String... args) throws Exception {
LOGGER.info("~~ STARTED ~~");
// Find employee-territories
EmployeeTerritory et = etService.findByTerritoryID(19713);
LOGGER.info("~~~~~~~~~~~~~~~~~~~~~~~~");
LOGGER.info("EmployeeID : "+et.getEmployeeID());
LOGGER.info("TerritoryID : "+et.getTerritoryID());
}
}
Main.java
#Configuration
#EnableAutoConfiguration
#EnableMongoRepositories("com.common.repository")
#ComponentScan({"com.common.main", "com.common.service"})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
application.properties
# MONGODB Config
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.uri=mongodb://localhost/boot
spring.data.mongodb.repositories.enabled=true
logging.level.com.altruista.guidingsigns=INFO
logging.level.org.springframework=INFO
DB Collection:
Only for clarifying purposes. I do not pretend this to be a definitive answer.
Maybe you have misunderstood me. You must respect java naming conventions for beans.
#Document(collection="employee-territories")
public class EmployeeTerritory {
#Id
private ObjectId id;
#Field
private String EmployeeID;
#Field(value="TerritoryID")
private Integer TerritoryID;
// setters and getters
}
Should be
#Document(collection="employee-territories")
public class EmployeeTerritory {
#Id
private ObjectId id;
#Field
private String employeeID;
#Field(value="TerritoryID")
private Integer territoryID;
// setters and getters
}
It may, I repeat, it may be the reason of your exception.
I was able to resolved this errors. Please see my code snippet below. As per spring data mongo docs (Refer section 12.1.1): http://docs.spring.io/spring-data/mongodb/docs/current/reference/html/
Your Mongo DB fields are using camel case letters and it's respective setters and getters, but as per doc "The default field name for identifiers is '_id' and can be customized via the #Field annotation."
For Ex: #Field('x') String id
Resulting Id-Fieldname in MongoDB ==> x,
Similar logic you need to apply for all your field if you're willing to get data by any field(s).
Use below and will work.
#Document(collection="employee-territories")
public class EmployeeTerritory {
#Id
private ObjectId id;
#Field("EmployeeID")
private Integer employeeID;
#Field("TerritoryID")
private Integer territoryID;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public Integer getEmployeeID() {
return employeeID;
}
public void setEmployeeID(Integer employeeID) {
this.employeeID = employeeID;
}
public Integer getTerritoryID() {
return territoryID;
}
public void setTerritoryID(Integer territoryID) {
this.territoryID = territoryID;
}
}

Class instance in the heap

I try to create a simple JPA application based on spring framework and Eclipselink JPA.
First of all initialize an EntityManagerBean:
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws Exception {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setJpaDialect(new EclipseLinkJpaDialect());
EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
vendorAdapter.setShowSql(true);
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.PostgreSQLPlatform");
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("org.egzi.diplom.model");
entityManagerFactoryBean.setLoadTimeWeaver(new SimpleLoadTimeWeaver());
//to update existing table (alter table)+
Properties additionalProperties = new Properties();
additionalProperties.setProperty("eclipselink.ddl-generation", "create-or-extend-tables");
additionalProperties.setProperty("eclipselink.ddl-generation.output-mode","database");
entityManagerFactoryBean.setJpaProperties(additionalProperties);
return entityManagerFactoryBean;
}
And in a DAO class a try to persist some entity:
#PersistenceContext
EntityManager entityManager;
#Transactional(propagation = Propagation.REQUIRED)
public void createTODO() {
Todo todo = new Todo();
todo.setSummary("a");
todo.setDescription("b");
entityManager.persist(todo);
}
And entity class:
#Entity
public class Todo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String summary;
private String description;
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "Todo [summary=" + summary + ", description=" + description
+ "]";
}
}
but when i run such code i have an exception:
java.lang.IllegalArgumentException: Object: Todo [summary=a, description=b] is not a known entity type.
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.registerNewObjectForPersist(UnitOfWorkImpl.java:4228)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:496)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:262)
at com.sun.proxy.$Proxy22.persist(Unknown Source)
at org.egzi.diplom.spring.GeneratorImpl.createTODO(GeneratorImpl.java:59)
and when i look into a eclipselink sources i find HashMap with Class as a key:
org.eclipse.persistence.internal.sessions.AbstactSession.getDescriptor(Class):
descriptor = this.descriptors.get(theClass);
When i started to debug my code i find that descriptors map already have a key of Todo.class. And i find that identityHash of class in HashMap and Todo.class is a different. I start my code from Inteliji IDEA.
What's wrong in my example?
UPDATE:
find that classes in descriptors HashMap have a link on org.springframework.instrument.classloading.SimpleInstrumentableClassLoader#14bb2297
but my Todo object have another one:
sun.misc.Launcher$AppClassLoader#58644d46
Try to add getter and setter for field ID too!
Fix exception by setting another LoadTimeWeaver - InstrumentationLoadTimeWeaver and exception is gone

Integrating JPA2.0 and Spring

last few hours I try to test with spring jpa 2.0 3.0
Finally I could recover objects persisted
but when I try to persist a new object I receive the following error message:
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:306)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.orm.jpa.JpaAccessor.translateIfNecessary(JpaAccessor.java:152)
at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:188)
at org.springframework.orm.jpa.JpaTemplate.flush(JpaTemplate.java:288)
at myPackage.testDAO.create(testDAO.java:33)
at myPackage.Main.main(Main.java:27)
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:789)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
at $Proxy21.flush(Unknown Source)
at org.springframework.orm.jpa.JpaTemplate$8.doInJpa(JpaTemplate.java:290)
at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:183)
... 3 more
my entity bean:
#Entity
public class Application implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="ID_APPLICATION")
private long idApplication;
#Temporal( TemporalType.DATE)
#Column(name="DATE_LIVRAISON")
private Date dateLivraison;
#Lob()
private String description;
#Column(name="NOM_APPLICATION")
private String nomApplication;
private String url;
//bi-directional many-to-one association to Test
#OneToMany(mappedBy="application")
private List<Test> tests;
public Application() {
}
public long getIdApplication() {
return this.idApplication;
}
public void setIdApplication(long idApplication) {
this.idApplication = idApplication;
}
public Date getDateLivraison() {
return this.dateLivraison;
}
public void setDateLivraison(Date dateLivraison) {
this.dateLivraison = dateLivraison;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String getNomApplication() {
return this.nomApplication;
}
public void setNomApplication(String nomApplication) {
this.nomApplication = nomApplication;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public List<Test> getTests() {
return this.tests;
}
public void setTests(List<Test> tests) {
this.tests = tests;
}
}
my repository:
#Repository
public class testDAO extends JpaDaoSupport implements ItestDAO {
#PersistenceContext
private EntityManager em;
public List<Application> findALL() {
// TODO Auto-generated method stub
return null;
}
public Application findById(long id) {
// TODO Auto-generated method stub
return getJpaTemplate().find(Application.class, id);
}
public void create(Application application) {
getJpaTemplate().persist(application);
}
}
findById method works normally (which reassures me that jpa configuration is correct), but when I run the create method I receive the above error message.
ApplicationContext context=new ClassPathXmlApplicationContext("application-context.xml");
testDAO dao=(testDAO)context.getBean("dao");
Application application=new Application();
application.setIdApplication(2);
application.setUrl("url");
application.setDescription("description");
application.setNomApplication("dsdsds");
dao.create(application);
any help are appreciated
Thank you for your help
In short, methods of EntityManager such as persist, remove, merge must be called inside a transaction, hence the error message which is actually self explaining:
javax.persistence.TransactionRequiredException: no transaction is in progress
Spring provides support for transaction management (see links below), including when writing tests... if you use one of the Spring testing class providing transactional support.
See also
10. Transaction Management
10.5 Declarative transaction management
10.5.6 Using #Transactional

Categories