Working with hibernate/DAO problems - java

Hello everyone here is my DAO class :
public class UsersDAO extends HibernateDaoSupport {
private static final Log log = LogFactory.getLog(UsersDAO.class);
protected void initDao() {
//do nothing
}
public void save(User transientInstance) {
log.debug("saving Users instance");
try {
getHibernateTemplate().saveOrUpdate(transientInstance);
log.debug("save successful");
} catch (RuntimeException re) {
log.error("save failed", re);
throw re;
}
}
public void update(User transientInstance) {
log.debug("updating User instance");
try {
getHibernateTemplate().update(transientInstance);
log.debug("update successful");
} catch (RuntimeException re) {
log.error("update failed", re);
throw re;
}
}
public void delete(User persistentInstance) {
log.debug("deleting Users instance");
try {
getHibernateTemplate().delete(persistentInstance);
log.debug("delete successful");
} catch (RuntimeException re) {
log.error("delete failed", re);
throw re;
}
}
public User findById( java.lang.Integer id) {
log.debug("getting Users instance with id: " + id);
try {
User instance = (User) getHibernateTemplate()
.get("project.hibernate.Users", id);
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
}
Now I wrote a test class(not a junit test) to test is everything working, my user has these fields in the database : userID which is 5characters long string and unique/primary key, and fields such as address, dob etc(total 15 columns in database table). Now in my test class I intanciated User added the values like :
User user = new User;
user.setAddress("some address");
and so I did for all 15 fields, than at the end of assigning data to User object I called in DAO to save that to database UsersDao.save(user); and save works just perfectly. My question is how do I update/delete users using the same logic?
Fox example I tried this(to delete user from table users):
User user = new User;
user.setUserID("1s54f"); // which is unique key for users no two keys are the same
UsersDao.delete(user);
I wanted to delete user with this key but its obviously different can someone explain please how to do these. thank you
UPDATE :
Do I need to set all 15 fields on User object to delete it like I did with save method ?

Having not looked at Hibernate for quite a while now, I can only hazard a guess at the problem.
It seems that you are creating a User object, but only populating the User ID field, so the persistence layer knows nothing about the actual User.
I would recommend using a retrieve function that can find the User with the given ID, and then pass that User into the delete method.
User u = UsersDao.findById("1s54f");
UsersDao.delete(u);
This should work, as the persistence layer will know about the User, so it has all of the details it needs to perform the delete.
However, a more efficient method would be to find a way of deleting a user by ID, so you do not have to query the database to get the instance of the User and then delete it.
Hope this helps.
Chris

In an ideal world you will have your model's business key as the database primary key and you'll not have this problem. But it ain't so, isn't it?
For you particular problem if you are very much sure that the userID is going to be unique then you can try this (taken from here):
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete User u where u.userID = :id";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "id", userID )
.executeUpdate();
tx.commit();
session.close();
But let me warn you. This kind of code is not good at all. For example what happens if you decide in future that the column you used in delete is no longer unique? Then you'll run into a very serious bug or a very bad case of refactoring. Either way the fool-proof (may not be efficient & may not be feasible) way is to delete records based on their primary key.

Check out the documentation. Get used to the concept of persistent, transient, and detached instances. To delete an instance, you call
session.delete(persistentInstance)
and to update (although you probably shouldn't need to use it), call
persistentInstance = session.merge(detachedInstance)
Shouldn't need to use update? No, because you just need to load/find an object first, and then modify it. Any modifications you make to a persistent object will automatically be saved back to the database.

In order to delete the user that its ID is "1s54f" you should create a delete HQL as follows:
public void delete(String id) {
log.debug("deleting Users instance");
try {
final String deleteQuery = "delete from User where id = :id";
final Query query = getSession().createQuery(deleteQuery);
final query.setString("id",id);
final int rowCount = query.executeUpdate(); // check that the rowCount is 1
log.debug("delete successful");
} catch (RuntimeException re) {
log.error("delete failed", re);
throw re;
}
}
Then you can use this method like:
userDao.delete("1s54f");
Hope this helps.

Hibernate is an object-relational mapper, that is, it translates between the world of relational databases and object-oriented Java code. Since primary keys are a database concept, it is hibernate's job to translate them into object-oriented terms (in this case: object identity).
That is, if you pass primary keys to hibernate, you are not using hibernate as intended; calling code should represent persistent data with mapped objects, not primary keys. This also allows hibernate to automatically guard against lost updates by checking version numbers.
The typical pattern therefore is to have the following signature:
interface UserDAO {
void delete(User user);
}
and require the DAOs caller to come up with a persistent object to pass to it. The caller might have such an object lying about from the current or a previous (now closed) session, after all, he did somehow learn about its primary key. If all else fails, you can use session.load(User.class, id) to ask hibernate for a proxy to pass to the delete method. (Note that one shouldn't use session.load if the object might no longer exist in the database.)

It's not necessary fetch a whole entity before removing it, neither create a hardcoded query for delete, nor setting every field in entity.
Maybe better way to do that is setting the id for entity and use Hibernate API itself.
If a specific dao is used to entity User, as described in question, try:
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
User user = new User();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(user, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
As can be seen, neither unnecessary operation in database is made.
And this can be used in generic flavor if GenericDao is implemented like:
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
Model entity = entityClass.newInstance();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(entity, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
Both ways, Dao must extend org.springframework.orm.hibernate4.support.HibernateDaoSupport to get advantages.
Here's a fragment of generic:
public class GenericDaoImpl<Model> extends HibernateDaoSupport implements GenericDao<Model> {
private Class<Model> entityClass;
public GenericDaoImpl() {
this.entityClass = (Class<Model>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
/* CRUD are implemented here */
public void remove(Serializable id) throws InstantiationException, IllegalAccessException {
Model entity = entityClass.newInstance();
getSessionFactory().getClassMetadata(getEntityClass()).setIdentifier(entity, id, (SessionImplementor) getSessionFactory().getCurrentSession());
getHibernateTemplate().delete(entity);
}
}

Related

Hibernate - 1 Class 2 Tables

I have a simple table and I want to store them in another table aswell (create a historic of users). Let's call it Users. I created a table exactly like Users (only different id name) called HISTORY_Users.
So I created the table and now I realize that I have 2 tables for 1 object. So how do I add my object only to the hist table?
I dont want to add them at the same time. I want to add the user to the hist only when he deletes the accounts.
I'm using Hibernate with xml mapping
//when the user deletes the account I call this function and pass the User
private static void addToHist(User User) {
//how do I add only to HIST_Users table??
Database.addToDatabase(user);
}
//Save the object to the database
public static void addToDatabase(Object object) {
SessionFactory factory = HibernateUtil.GetSessionFactory();
Session session = factory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
session.save(object);
tx.commit();
}catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
}finally {
session.close();
}
}
Maybe better solution will be use the Hibernate Envers: http://hibernate.org/orm/envers/
You can configure when you want to put it into history: on create, update or delete.
Tables are created automatically, you need to add some annotations only like #Audited etc.

How to update postgres table with Spring Boot JPARepository?

I have a Spring boot application thats committing object to Postgres DB using JPARepository. The code for my repository is as below:
public interface TestObjectDao extends JPARepository<TestObject, Long> {
List<TestObject> findByTestRefIdAndTestType(String testRefId,
Short testType);
TestObject save(TestObject testObject);
}
When I want to create, In my service implementation (implementing above interface) I used "save" method. But, when I try to update, it neither creates entry nor update it.
How can I update records in Postgres database? Below is my code snippet using for update:
#Component
public class TestObjectServiceImpl implements TestObjectService {
#Inject
private TestObjectDao TestObjectDao;
#Override
public TestObjectResponse createTestObject(String testRefId, String testType, TestObject testObject) throws Exception{
--
--
--
try {
//update object
testObject = TestObjectDao.save(testObject);
}
}
catch (Exception ex) {
throw new Exception("Object could not be modified");
}
TestObjectResponse testObjectResponse = new TestObjectResponse();
testObjectResponse.setVal(testObject);
return testObjectResponse;
}
}
I have gone through all related posts but didn't get satisfactory resolution.
Spring detects wether it needs to save or create an entity by checking it's ID.
In your case, you need to select it first, so Spring will populate the entity properly:
try {
testObject = TestObjectDao.findOne(testRefId);
//update object
testObject = TestObjectDao.save(testObject);
}
Please refer section 2.2.1:
http://docs.spring.io/spring-data/jpa/docs/1.4.1.RELEASE/reference/html/jpa.repositories.html
Note that if only some columns are to be updated, it's much more efficient to use:
#Modifying
#Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

How to optimize hibernate method call in loop?

I have a java web application built using spring+hibernate.
I have code like this:
for (Account account : accountList){
Client client = clientService.findById(account.getFkClient()); // fkClient is foreign key to Client
if (client != null) {
...
anObject.setName(client.getName());
anObject.setAccountNo(account.getAccountNo());
...
}
else {
...
anObject.setAccountNo(account.getAccountNo());
...
}
...
}
accountList is a List of Account entity that retrieved from hibernate call. Inside the for loop, a Client entity is retrieved from account using hibernate call inside clientService.findById method.
These are the class involved to the call:
public class ClientService implements IClientService {
private IClientDAO clientDAO;
...
#Override
public Client findById(Long id) throws Exception {
return clientDAO.findById(id);
}
}
public class ClientDAO extends AbstractHibernateDAO<Client, Long> implements IClientDAO {
#Override
public Client findById(Long id) throws Exception {
return super.findById(id);
}
}
public class AbstractHibernateDAO<T,Y extends Serializable> extends HibernateDaoSupport {
protected Class<T> domainClass = getDomainClass();
private Class<T> getDomainClass() {
if (domainClass == null) {
ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();
domainClass = (Class<T>) thisType.getActualTypeArguments()[0];
}
return domainClass;
}
public T findById(final Y id) throws SystemException {
return (T) this.execute(new HibernateCallback<T>() {
#Override
public T doInHibernate(Session session) throws HibernateException, SQLException {
return (T) session.get(domainClass, id);
}
});
}
}
Note: clientService and clientDAO are spring beans object.
My question is how to optimize the clientService.findById inside the loop with hibernate? I feel the findById call make the looping process become slower.
The accountList usually contains 7000+ records, so I need something like pre-compiled query mechanism just like PreparedStatements in jdbc. Is it possible to do this with hibernate?
Note: the code above has been simplified by removing unrelated parts, the method, variable and class name are made fictious for privacy reason. If you find a typo, please let me know in the comment section since I typed the code manually.
In Hibernate/JPA you can write queries with Hibernate Query Language/ JPA query language and create NamedQueries. NamedQuery is compiled when server is started so you can consider it like some kind of prepared statement.
You can try to write HQL query which can get all entity instances with single query.
I will give you example in JPQL but you can write it with HQL as well.
#NamedQueries({
#NamedQuery(name = "QUERY_BY_ID",
query = "SELECT u from SomeEntity se WHERE se.id IN (:idList)"),
})
class SomeEntity {
}
class SomeEntityDao {
public List<SomeEntity> findIdList(List<Long> idList) {
Query query = entityManager.createNamedQuery("QUERY_BY_ID");
query.setParameter("idList", idList);
return query.getResultList();
}
}
I found the best solution. I put the query that select columns from table Account and Client joined together into a View (VIEW_ACCOUNT_CLIENT), then I made entity class (AccountClientView) for the view and fetch it using hibernate, the result is wow, it boosts the performance drastically. Using the real code, it could takes about 15-20 minutes to finish the loop, but using View, it only takes 8-10 seconds
#Entity
#Table(name = "VIEW_ACCOUNT_CLIENT")
public class AccountClientView implements Serializable {
...
}
It's not clear what you want to achieve. I wouldn't do service calls in a loop. Why don't you use a NamedQuery?
Retrieve all Clients attached to the given Accounts, then iterate over that list of Clients.
SELECT c from Client c JOIN c.account a WHERE a.id IN (:accounIds)
But it really depends on the business requirement!
Also it's not clear to me why don't you just call:
Client client = account.getClient();
You might want to load your accountList with the clients already fetched in. Either use eager fetching, or fetch join. If the Account entity does not contain a Client, you should have a very good reason for it.

javax.jdo.JDODetachedFieldAccessException when I ask for a List<T> using JPA and Datastore

im having javax.jdo.JDODetachedFieldAccessException when i want to, after retrieve all of my Entites as a List in my DAO implementation, ask for one atrribute object from my Entity.
public List<T> findAll() {
this.entityManager = SingletonEntityManagerFactory.get().createEntityManager();
EntityTransaction tx = this.entityManager.getTransaction();
try {
tx.begin();
return this.entityManager.createQuery(
"select f from " + clazz.getName() + " as f").getResultList();
}finally {
tx.commit();
if (tx.isActive()) {
tx.rollback();
}
this.entityManager.close();
}
}
for instance, supposing T has a property of class A that is already an Entity persisted, i can't get A after having List
But i don't have this problem if I only look for a single Entity by Id. I obtain my entity and I can ask without problems for its attribute objects already persisted
public T getById(final Key id) {
return getEntityManager().find(clazz, id);
}
now i can do
A a= t.getA();
How can I write my implementation of findAll() avoiding this error? maybe another Component instead of EntityManager? How can i make it generic, and not having to implement specific code for specific type of entities?
What you do there doesn't make sure the field is loaded before leaving that method, so either access it, or make sure it is fetched by default.

How to persist a JPA autogenerated value before commit?

Hi I am beginner on the JPA world, I have a question on the auto-generated id. We are using OpenJPA, My application requires that one operation which creates bunch of related objects must be inside a single transaction which will be part of global transaction (XA). I am struggling in get the auto-generated id and use it to set values in other object. Here is the snapshot:
#ENTITY
#Table(name="TDepart")
class Department{
private long id;
#GeneratedValue(strategy= GenerationType.TABLE)
public long getId();
}
//And some classes like
class Professor {
void setDepartmentId(long id);
}
Now I have a business operation:
void doSomething()
{
Department depart = new Department();
handleProfessors (depart);
handleStudent (depart);
//and someother rountines need to refer department
}
//sample code which will getId
void handleProfessors(Department depart)
{
Professor p = new Professor ();
p.setDepartmentId(depart.getId);
}
So the Department.getId() will be called several times. The doSomething() will be in a single managed transaction, but the GeneratedValue will use an unmanaged tx. Now may problem is: whenever the getId is called, it will return a new value, and when the department is final persisted, the id is the latest number, so all other objects refer to an non-exists department. Is there anyway to handle this so that the id is (kindof) persist?
I have a loose requirement solution, which will create an dummy department first and persist it, so the ID is not change. The code is similar to this:
void doSomething()
{
Department depart = createEmptyDepartment(); // always new tx so department is created;
try {
reallyDoSomehing(); // tx required so it is part of global tx
}
catch (SomeException e) {
removeEmptyDepartment(depart);
}
Now I do not know how I can set the tx for removeEmptyDepartment(), if is required it will use the global request so it will be rollback as well. If it is new tx it will cause a deadlock since reallyDoSomething() will lock the db row.
Please, give me some ideas on how to solve it.
Thanks,
Howard.
I don't fully understand your issue, but I'm thinking that rather than setting the departmentId in your professor class, you should be setting the Department instead
i.e.
void setDepartmentId(long id);
change to
void setDepartment(Department d);
The id components should be handled automatically by the entity manager

Categories

Resources