I am quite a newbie to Spring JPA in cooperation with ObjectDB database, but I have encountered a problem that I cannot work out.
I have an application written with the mentioned technologies and it works OK, it persists new entities etc. (thus I think there is no problem with the configuration beans), except for updating even the simplest OneToMany/ManyToOne relations. Those updates are not persisted to the database and I cannot figure out why. Here's the snippet of my code:
Entity Team (1:N):
#Entity
public class Team implements Serializable {
...
List<Player> squad;
...
#OneToMany(mappedBy="team", cascade=CascadeType.PERSIST)
public List<Player> getSquad() {
return squad;
}
...
}
Entity Player (N:1)
#Entity
public class Player implements Serializable {
...
private Team team;
...
#ManyToOne
public Team getTeam() {
return team;
}
...
}
Here is a snippet from controller using both DAO objects and the problem:
public ModelAndView addPlayer(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
Team t = teamDao.getTeamById(1); // retrieves an object with ID=1
Player p = playerDao.getPlayerById(1); // retrieves a player with ID=1
t.getSquad().add(p); // adds a player to the squad -> working fine but does not persist
System.out.println("Size of squad: " + t.getSquad().size()); // the player is there
...
return new ModelAndView("index.jsp", "team", t);
}
When I try to list all players in the team inside the index.jsp page or try to add another player the same way, the squad is always empty - nothing persisted to the database. Neither the team object, nor the player object. What do I do wrong?
Any help would be appreciated. Thanks.
EDIT: here is my persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="NewPU" transaction-type="RESOURCE_LOCAL">
<provider>com.objectdb.jpa.Provider</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="C:/file.odb" />
<property name="javax.persistence.jdbc.user" value="admin"/>
<property name="javax.persistence.jdbc.password" value="admin"/>
</properties>
</persistence-unit>
P.S. The absolute path "C:/file.odb" is only for demonstration purposes.
and here is Spring configuration:
<mvc:annotation-driven />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="NewPU" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
CascadeType.PERSIST cascades the persistens of a new object but you load a player from the database and attach the player to your team. If you want to cascade that you have to add the CascadeType.MERGE
cascade = {CascadeType.PERSIST, CascadeType.MERGE}
You may have a look at CascadeType.ALL.
Related
I face following issue while trying to write some JBehave BDD test. Scenario is the following:
Due to an MQ message some specific records should be saved into my database and some should be deleted from it. In my #When step I send the given message and in my #Then step I have some asserts to controll the result.
I face issue with the persist and update methods of Hibernate JPA, because it happens just after my code runs to my #Then step and so I got always false result. When I check it in debug mode, and check every recorsd which should be updated / deteled, they are fine.
I assume my asserts should be executed just after the transaction is committed into the database - but this is not the case as I can see.
Is there a way to set a delay or sleep time between database transactions?
Here is e.g. my delete method in my main project:
public void deleteByAbcId(final String Id) {
getEm().createNamedQuery(TABLE.NAMED_QUERY_DELETE_BY_ABC_ID)
.setParameter(Table.QUERY_PARAM_ABC_ID, Id)
.executeUpdate();
}
And in my BDD project I set up database connection as follows:
public class DatabaseService implements Closeable {
private EntityManagerFactory emf = null;
private EntityManager em = null;
/**
* This creates an entity manager based on the db connection parameters received in the argument
*/
public DatabaseService(Properties configuration) {
emf = Persistence.createEntityManagerFactory("project-pu", configuration);
em = emf.createEntityManager();
em.getTransaction().begin();
}
/**
* Returns the entity manager for the db connection
*/
public EntityManager getEm() {
return em;
}
In my assertion I use the following query to check if the given record was successfully removed from the db:
assertNull(dbHelper.findTableIdBasedOnAbcId(Serenity.sessionVariableCalled(ABC_ID)));
My dbHelper class looks like as following:
public class DbHelper {
private DatabaseService database;
private Configuration config = Configuration.getInstance();
public DbHelper() {
database = new DatabaseService(config.getDbProperties());
}
public String findTableIdBasedOnAbcId(String Id) throws Exception {
String query = "SELECT id FROM TABLE WHERE ABC_ID = ?1";
Query queryResult = database.getEm().createNativeQuery(query);
queryResult.setParameter(1, Id);
List<Long> list = (List<Long>) queryResult.getResultList();
if (!list.isEmpty()) {
return String.valueOf(list.get(0));
}
return null;
}
It always retunr the given record of the database, which gets not deleted.
This is the persistence xml of my main project:
<persistence-unit name="aaa-pu" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>jdbc/aaaa-ds</jta-data-source>
<mapping-file>META-INF/orm.xml</mapping-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.id.new_generator_mappings" value="false"/>
<property name="tomee.jpa.factory.lazy" value="true"/>
</properties>
</persistence-unit>
And this is the one of my BDD project:
<persistence-unit name="project-pu" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.connection.autocommit" value="false" />
<property name="hibernate.hbm2ddl.auto" value="none" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.generate_statistics" value="false"/>
</properties>
</persistence-unit>
Any idea, how to set transactions not to execute the assert method before the executeUpdate of Hibernate runs?
I tried to call the entityManager.getTransaction.commit method and then start a new one every time but no change.
Is there a way to lock the transaction until the executeUpdate is done and just after that allowing the select query? Or is it possible with JBehave to give a waiting or sleep time between steps?
Thank you.
I created a Spring project and I would like to create multiple queries on a single file. After googling, I found this link https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-creating-database-queries-with-named-queries/
So, I should create the file orm.xml:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
version="2.0">
<named-query name="Todo.findByTitleIs">
<query>SELECT t FROM Todo t WHERE t.title = 'title'</query>
</named-query>
</entity-mappings>
Then, the interface TodoRepository.java
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
interface TodoRepository extends Repository<Todo, Long> {
#Query(nativeQuery = true)
public List<Todo> findByTitleIs();
}
The spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="employeeDAO" class="com.journaldev.spring.jdbc.dao.EmployeeDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="employeeDAOJDBCTemplate" class="com.journaldev.spring.jdbc.dao.EmployeeDAOJDBCTemplateImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="todoRepositoryBean" class="com.journaldev.spring.jdbc.dao.TodoRepositoryBeanImpl">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/springdb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
</beans>
The class SpringMain.java is:
public class SpringMain {
public static void main(String[] args)
{
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
EmployeeDAO employeeDAO = ctx.getBean("todoRepositoryBean", EmployeeDAO.class);
List<Employee> les = employeeDAO.getAll();
System.out.println("The list size is: + les.size()");
ctx.close();
}
}
I think it was useful but I found a problem: How can I use the named query Todo.findByTitleIs on main class?
The tutorials of Petri Kainulainen are really good and you should achieve your goal by reading this one:
https://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-eight-adding-functionality-to-a-repository/
He explaines how to implement the TodoRepository class.
To use queries defined in your repository, if you are developing a rest web app, you could use a #Controller class to handle the single functionalities (e.g. TodoController). In the controller you can autowire the repository:
#Autowired
private TodoRepository todoRepository;
and then use it in your methods:
public void doSomething() {
List<Todo> todoList = todoRepository.findByTitleIs();
}
Remember that you can autowire beans only in spring managed classes (Repository, Service, Controller, Component, Configuration ecc)
Otherwise you could get the repository directly from the ApplicationContext, but it's not recommended:
Why is Spring's ApplicationContext.getBean considered bad?
Personally i use this method only for testing purpose (to create a main test class).
Then i suggest you to write your queries directly in the repository interface, to me it's much simplier (this way you can avoid using the orm.xml file). For example:
#Repository
public interface TodoRepository extends JpaRepository<Todo, Long> {
// define your custom query
#Query("SELECT t FROM Todo t WHERE t.title = :title")
public List<Todo> findByTitleIs(#Param("title") String title);
// write here all the Todo queries
}
You can also use the query creation from method names mechanism, and write the previous query like this:
public List<Todo> findByTitle(String title);
http://docs.spring.io/spring-data/jpa/docs/1.4.3.RELEASE/reference/html/jpa.repositories.html
I use JPA + Hibernate + Spring for simple job.
I wrote next modules:
Service:
#Autowired
private VolMng volMng;
#Service
public class Dist {
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void gen() {
MeterLog mLog = em.find(MeterLog.class, 3625190);
Lst volTp = lstMng.findByCD("Fact Vol");
Vol vol = new Vol((MeterLog) mLog, volTp, 7777.77d);
volMng.add(vol);
//point-1:
for (Vol a : mLog.getVol()) {
System.out.println("found="+a.getId()+" vol="+a.getVol1());
}
...
...
Service:
#Service
public class VolMngImpl implements VolMng {
#Autowired
private VolDAO vDao;
public void add(Vol vol) {
vDao.add(vol);
}
}
DAO:
#Repository
public class VolDAOImpl implements VolDAO {
#PersistenceContext
private EntityManager em;
public void add(Vol vol) {
em.persist(vol);
}
}
I am trying to add some records to child entity Vol.
But after volMng.add(vol) at point-1 I don't see any added records (child entities).
Why?
upd
Of course I see these records after end of transaction, but why I can't do it before??? They must be in memory cache...
upd2
My spring.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd">
<!-- *******************************
***** CACHE CONFIGURATION *****
******************************* -->
<cache:annotation-driven cache-manager="cacheManager" />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache"/>
</bean>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
<property name="shared" value="true"/>
</bean>
<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
<property name="URL" value="jdbc:oracle:thin:#192.168.1.1:1521:DEV" />
<property name="user" value="ora"/>
<property name="password" value="ora"/>
<property name="connectionCachingEnabled" value="true"/>
</bean>
<bean
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
id="entityManagerFactory">
<property name="dataSource" ref="dataSource" />
</bean>
<context:component-scan base-package="com.ric.bill" />
<context:component-scan base-package="com.ric.bill.dao.impl" />
<context:component-scan base-package="com.ric.bill.mm.impl" />
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="proxy" transaction-manager="transactionManager"/>
<context:spring-configured />
<context:annotation-config />
</beans>
Collections are not automatically updated in the session.
It will appear once you reload everything, but you have to manually add it to the children collection of the owning object in case you need it in the current session.
To solve this problem I have to add children entity to the parent, like this:
MeterLog mLog = em.find(MeterLog.class, 3625190);
Lst volTp = lstMng.findByCD("Fact Vol");
Vol vol = new Vol((MeterLog) mLog, volTp, 7777.77d);
mLog.getVol().add(vol);
//point-1:
for (Vol a : mLog.getVol()) {
System.out.println("found="+a.getId()+" vol="+a.getVol1());
}
To answer the actual question -- why can't you see the newly added entity...
You run a select statement and put the results in a java object:
MeterLog mLog = em.find(MeterLog.class, 3625190);
The find ultimately creates the select statement and the result set is transformed into the MeterLog object via hibernate according to the jpa annotations you've used to define the mapping. From this point on your code doesn't change this object. You create a new entity with this code:
volMng.add(vol);
which directly persists the new entity in the database. But, this is after the select has already returned the data you asked for. You don't do anything to change or refresh the result set you've already acquired. The underlying datastore (oracle, mysql, whatever) doesn't remember that you've queried it and are holding a copy of its data, so it can do nothing to alert you to the fact that the underlying data has changed.
As you mention in your answer, you can add the new entity directly to the object's collection:
mLog.getVol().add(vol);
And this will, in certain circumstances, persist the new entity to the database when the transaction successfully completes, but also allows you to see the entity when you're looping through the collection, hence your observation. You have broken the law of Demeter by doing this here however -- it should be Vol's responsibility to add itself to the collection of volumes of the given MeterLog when it is created.
Setup
I'm Using JPA 1.0, Spring 3.0.6 and hibernate 3.2.1 on JBoss 4.3.2. There are some EJBs which at some point call a DAO which tries to persist two entities.
Problem
The result is quite unexpected: The entities are not managed after calling .persist(entity).
The Funny thing is: A quite similar test app, which uses the same jars containing DAOs, Beans persistence.xml as the server application, persists everything just fine. The beans are managed after calling .persist().
The entities
I have a class Subscriber that has a SubscriberState.
#Entity
#Table(name = "subscriber")
public class Subscriber implements java.io.Serializable {
// all the other stuff
#ManyToOne
#JoinColumn(name = "status")
private SubscriberState state;
}
For legacy reasons the SubscriberState has a relation Subscriber.
#Entity
#Table(name = "subscriber_state")
public class SubscriberState implements java.io.Serializable {
// ...blah
#ManyToOne
#JoinColumn(name = "subscriber", nullable = false)
private Subscriber subscriber;
}
what i do to persist
Now i try to persist a subscriber, it will be unmanaged afer persist():
final Subscriber subscriber = new Subscriber();
// set up...
entityManager.persist(subscriber);
entityManager.contaons(subscriber); //will yield FALSE
Adding a SubscriberState will work. But only one side of the relation will be stored to DB:
SubscriberState subscriberState = new SubscriberState();
subscriberState.setSubscriber(subscriber);
entityManager.persist(subscriberState);
entityManager.merge(subscriber);
where SubscriberState.setSubscriber looks like this:
public void setSubscriber(final Subscriber subscriber) {
this.subscriber = subscriber;
subscriber.setState(this);
}
A look at the database shows me, that the SubscriberState knows it's Subscriber, but the Subscriber does not know it's State.
the config
Here's the (now modified and stripped down) persistence.xml:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
org.hibernate.ejb.HibernatePersistence
java:jdbc/SODS
<properties>
<property name="hibernate.dialect" value="${db.hibernate.dialect}" />
<property name="hibernate.validator.apply_to_ddl" value="false" />
<property name="hibernate.validator.autoregister_listeners" value="false" />
<property name="hibernate.cache.provider_class" value="org.jboss.ejb3.entity.TreeCacheProviderHook" />
<property name="hibernate.treecache.mbean.object_name" value="jboss.cache:service=EJB3EntityTreeCache" />
<property name="jboss.entity.manager.jndi.name" value="java:/SOEntityManager-${app.version}" />
<property name="jboss.entity.manager.factory.jndi.name" value="java:/SOEntityManagerFactory-${app.version}" />
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory" />
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
</properties>
<class>com.stackoverflow.some.classes.i.cant.show.Here</class>
and here are the relevant parts of my spring context:
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:/SOEntityManagerFactory-${app.version}" />
<jee:jndi-lookup id="entityManager" jndi-name="java:/SOEntityManager-${app.version}" />
<jee:jndi-lookup id="soDataSource" jndi-name="java:jdbc/SODS" />
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg ref="soDataSource" />
</bean>
<context:component-scan base-package="blah - secret" />
<import resource="daoContext.xml" />
</beans>
Any ideas, why this config won't work?
I don't see use of #Transaction in your DAO or service layer
Adding a SubscriberState will work. But only one side of the relation will be stored to DB
You should use cascade option in
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "subscriber", nullable = false)
private Subscriber subscriber;
This will save both side of the relationship in persist operation
Also what I see is two unidirectional relationship, not one bidireccional, try to use mappedBy
#ManyToOne
#JoinColumn(name = "status")
private SubscriberState state;
Unless that is not what you want, but you should reconsider your design to made one side OneToMany and the other side ManyToOne, use mappedBy to define the source (owner) of the relationship
This happened to my application, JPA with MySQL. Load the appropriate database driver class in the property file.
This is my first real exposure to JPA and I'm trying to run the simplest of update statements. I'm running inside a jBoss 7.1.x server using Hibernate as JPA implementation. Any help would be greatly appreciated..
Here is my producer method for EntityManager:
#ApplicationScoped
public class EntityManagerProducer
{
#PersistenceContext
private EntityManager entityManager;
#Produces
#RequestScoped
public EntityManager getEntityManager()
{
return entityManager;
}
}
Here is the DAO method that tries to perform the update:
#Inject EntityManager em;
public void updateRequestStatus(String requestNumber, String newStatus)
{
em.getTransaction().begin();
ServiceRequestEntity serviceRequestToUpdate = em.find(RequestEntity.class, requestNumber);
requestToUpdate.setStatus(newStatus);
em.merge(serviceRequestToUpdate);
em.getTransaction().commit();
}
Here is persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="database" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:/jdbc/myDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="false" />
<property name="hibernate.use_sql_comments" value="true" />
</properties>
</persistence-unit>
</persistence>
Here is the error message (can provide full stacktrace if needed -it chokes on the "merge" line):
javax.persistence.TransactionRequiredException: JBAS011469: Transaction is required to perform this operation (either use a transaction or extended persistence context)
....
at org.jboss.weld.proxies.EntityManager$-1727851269$Proxy$_$$_WeldClientProxy.merge(EntityManager$-1727851269$Proxy$_$$_WeldClientProxy.java) [weld-core-1.1.5.AS71.Final.jar:]
#Inject annotation on EntityManager I don't think will work. With your current setup I believe you want to inject your EntityManagerProducer class (which is not used in your DAO), and then call your getEntityManager method on it.
The other option would just be to use the #PersistenceContext in your DAO.
You also may want to specify the name of your persistence context as specified in your persistence.xml like:
#PersistenceContext(name="database")