The following code:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test.odb");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Point p = new Point(0, 0);
em.persist(p);
em.getTransaction().commit();
em.getTransaction().begin();
Query query = em.createQuery("UPDATE Point SET x = 1001 where x = 0");
int updateCount = query.executeUpdate();
em.getTransaction().commit();
TypedQuery<Point> myquery = em.createQuery("SELECT p from Point p where p.x = 1001", Point.class);
List<Point> results = myquery.getResultList();
System.out.println("X coordinate is: " + results.get(0).getX());
em.close();
prints out: X coordinate is: 0
which is wrong because X coordinate should be 1001
But if I change the code to:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test.odb");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Point p = new Point(0, 0);
em.persist(p);
em.getTransaction().commit();
em.getTransaction().begin();
Query query = em.createQuery("UPDATE Point SET x = 1001 where x = 0");
int updateCount = query.executeUpdate();
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
TypedQuery<Point> myquery = em.createQuery("SELECT p from Point p where p.x = 1001", Point.class);
List<Point> results = myquery.getResultList();
System.out.println("X coordinate is: " + results.get(0).getX());
em.close();
The result is same as expected:
X coordiate is: 1001
What have I done wrong in the first code snippet?
UPDATE queries bypass the EntityManager, which means that the EntityManager may not have an up to date view of the real objects in the database.
As explained in the UPDATE queries page in the ObjectDB Manual:
"Updating entity objects in the database using an UPDATE query may be slightly more efficient than retrieving entity objects and then updating them, but it should be used cautiously because bypassing the EntityManager may break its synchronization with the database. For example, the EntityManager may not be aware that a cached entity object in its persistence context has been modified by an UPDATE query. Therefore, it is a good practice to use a separate EntityManager for UPDATE queries."
Using a separate EntityManager is exactly what you did by closing and opening a new EntityManager in your revised code.
Alternatively, if you want to use the same EntityManager, you may clear its persistence context (i.e. its cache), after running the UPDATE query and before running the SELECT query.
Related
I'm learning JPA. In the code below I'm trying to persist Point, then list Points to check and it turns out that it haven't been saved. Why?
Thank you!
PersistenceProvider provider = new PersistenceProvider();
EntityManagerFactory emf = provider.createEntityManagerFactory("sample", null);
EntityManager em = emf.createEntityManager();
// DROP TABLE POINT
// CREATE TABLE POINT (ID BIGINT NOT NULL, X DOUBLE, Y DOUBLE, PRIMARY KEY (ID))
// DELETE FROM SEQUENCE WHERE SEQ_NAME = 'SEQ_GEN'
// SELECT * FROM SEQUENCE WHERE SEQ_NAME = 'SEQ_GEN'
// INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0)
Point p = new Point(1, 1);
em.persist(p);
// UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
// bind => [2 parameters bound]
// SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
// bind => [1 parameter bound]
em.close();
EntityManager em2 = emf.createEntityManager();
Query query = em2.createQuery("select p from Point p");
List list = query.getResultList();
// SELECT ID, X, Y FROM POINT
System.out.println("Found objects: " + list.size());
// Found objects: 0
for (Object elt: list){
System.out.println(elt);
}
Whole project: https://github.com/greenmarker/EclipseLinkTest
Since you have no transaction there, then nothing is committed.
Some JPA providers will allow "non-transactional" writes (what you're trying to do there, e.g DataNucleus JPA), but many won't, so you need to do
em.getTransaction().begin();
... (persist, merge, remove calls)
em.getTransaction().commit();
if you want vendor-independent persistence code
Is it possible to execute an update while using Criteria in Hibernate? For example:
Session session = getSession();
Criteria crit = session.createCriteria(User.class);
crit.add(Restrictions.eq("token", sessionToken));
User user= new User();
Transaction tx = session.getTransaction();
try
{
tx.begin();
session.updateWithCriteria(user, crit); //my imaginary function
tx.commit();
}
catch (Exception e)
{
e.printStackTrace();
tx.rollback();
}
session.close();
There is a very powerful feature called:
15.4. DML-style operations
small cite from doc:
... However, Hibernate provides methods for bulk SQL-style DML statement execution that is performed through the Hibernate Query Language...
So, while this is not about criteria - we still can use our domain model for querying, because it is about HQL. This is a snippet showing the power:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
SUMMARY: Having that in place:
we can use query to filter results
we can apply bulk update on it
we won't need to load these rows in memory, into the session...
Now we can do something like this for bulk update and delete. New api's released for criteriaUpdate and CriteriaDelete
CriteriaBuilder cb = this.em.getCriteriaBuilder();
// create update
CriteriaUpdate<Order> update = cb.createCriteriaUpdate(Order.class);
// set the root class
Root e = update.from(Order.class);
// set update and where clause
update.set("amount", newAmount);
update.where(cb.greaterThanOrEqualTo(e.get("amount"), oldAmount));
// perform update
this.em.createQuery(update).executeUpdate();
First you should get the object then modify and update:
Query q = session.createQuery("from StockTransaction where tranId = :tranId ");
q.setParameter("tranId", 11);
StockTransaction stockTran = (StockTransaction)q.list().get(0);
stockTran.setVolume(4000000L);
session.update(stockTran);
If you want to use partial/dynamic update feature then put
#org.hibernate.annotations.Entity(
dynamicUpdate = true
)
annotation on top of the dao class.
Example from: http://www.mkyong.com/hibernate/hibernate-dynamic-update-attribute-example/
Note: The Question is "with criteria" but the accepted answer is NOT "with criteria" but SQL.
I don't know if this is really a bug ... it seems that something remains "open".
I'm using EclipseLink 2.5 with com.microsoft.sqlserver.jdbc.SQLServerDriver.
In the second call at the createEntityManagerFactory method a wrong password is ignored and everything works as well ...
Do I have to reset or clean some kind of Connection or Session Object ?
String userId = "sa";
String psw = "rightPassword";
Map<String, Object> paramsConnect = new HashMap<String, Object>();
paramsConnect.put("javax.persistence.jdbc.user", userId);
paramsConnect.put("javax.persistence.jdbc.password", psw);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PU", paramsConnect);
EntityManager em = emf.createEntityManager();
Query q = em.createNativeQuery("SELECT * FROM Tab1");
List<Object[]> rows = q.getResultList();
System.err.println(rows.get(0));
em.clear();
em.close();
emf.close();
psw = "wrongPassword";
EntityManagerFactory emf1 = Persistence.createEntityManagerFactory("PU", paramsConnect);
EntityManager em1 = emf1.createEntityManager();
Query q1 = em1.createNativeQuery("SELECT * FROM Tab2");
List<Object[]> rows1 = q1.getResultList();
System.err.println(rows1.get(0));
If you want to change the value with key 'javax.persistence.jdbc.password' in your HashMap you need to change the line:
psw = "wrongPassword";
with the line:
paramsConnect.put("javax.persistence.jdbc.password", "wrongPassword");
The method put will replace the value of an existing key and will create it if doesn't exist.
Im querying a database using JPQL but I cannot retrieve the 'Report' table's rows using List. This is a section of my code:
...
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Query query = em.createQuery("SELECT r.title, r.company FROM Report as r");
List<Report> itemList = query.getResultList();
for (Report item : itemList)
{
System.out.println("Item: " + item.getCompany());
}
The output is:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to maps.Report at hello.Test.main(Unknown Source)
Java Result: 1
What am I doing wrong? Why I am not allowed to do the casting?
Your query doesn't select instances of Report. It selects two fields: r.title and r.company. In this case, JPA returns a list of Object[]. Each Object[] of the list contains two elements: the title and the company.
Use select r from Report r to select Report instances.
You need to create a TypedQuery.
String sql = "select r from Report r";
TypedQuery<Report> query = em.createQuery(sql, Report.class);
List<Report> reports = query.getResultList();
I have 1M rows in a table and I want to get all of them. But when I try to get all rows with jpa by pagination then I get java heap error. Do you think that am I missing something? Any advice
int counter = 0;
while (counter >= 0) {
javax.persistence.EntityManager em = javax.persistence.Persistence
.createEntityManagerFactory("MyPU")
.createEntityManager();
Query query = em.createQuery("select m from mytable m");
java.util.Collection<MyEntity> data = query
.setFirstResult(counter).setMaxResults(1000).getResultList();
for(MyEntity yobj : data){
System.out.println(obj);
}
counter += 1000;
data.clear();
em.clear();
em.close();
}
Since you use native SQL anyway, can't you specify the LIMIT :counter, 1000 (or ROWNUM BETWEEN :counter AND 1000 if using Oracle) directly in your SQL statement?
Note that you create new instance of EntityManagerFactory at each iteration, but don't close it. Perhaps it would be better to use a single factory instead:
int counter = 0;
EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("MyPU");
while (counter >= 0) {
javax.persistence.EntityManager em = emf.createEntityManager();
...
}