I have a database with two tables, Account and Favorites. Favorites is a many-to-many table. It holds:
listowner (foreign key referencing the Account primary key)
favorite (also a foreign key referencing the Account primary key)
Favorites does not have its own class in my program. I only have Account.java, which holds two sets.
private Set<Account> favorites;
private Set<Account> listOwner;
//the getters and setters for these sets
The relevant mapping file:
<set name="favorites" table="favorites" inverse="true" cascade="all">
<key column="listowner" />
<many-to-many column="favorite" class="Models.Account" />
</set>
<set name="listOwner" table="favorites" cascade="all">
<key column="favorite" />
<many-to-many column="listowner" class="Models.Account" />
</set>
Now, saving to the database works fine. I can save a favorite account with a listowner and see him appear when directly accessing the database. But I can't get this information out of the database again. I want a list of all favorites of an account. In SQL, this would be:
SELECT favorite
FROM favorites
WHERE listowner = "Bob"
My current attempt:
public static List<Account> getFavorites(Account account)
{
List<Account> list = null;
Transaction tx = null;
try
{
tx = session.beginTransaction();
list = session.createQuery("from Account a where a.listOwner.accountName = :name").setParameter("name", account.getAccountName()).list();
tx.commit();
} catch (Exception e)
{
if (tx != null)
{
tx.rollback();
}
System.out.println("getFavorites failed");
e.printStackTrace();
} finally
{
return list;
}
}
According to the debugger, it's failing on
list = session.createQuery("from Account a where a.listOwner.accountName = :name").setParameter("name", account.getAccountName()).list();
What am I doing wrong? I'm not getting any exceptions.
Your query is wrong. a.listOwner is of type Set<Account>. And a Set<Account> doesn't have any accountName property. To be able to add restrictions on an element of a.listOwner, you need an explicit join:
select a from Account a
inner join a.listOwner owner
where owner.accountName = :name
That said, your whole method should simply be replaced by
return account.getFavorites();
Related
Using Hibernate To Save Failed
When I use hibernate to save an entity to the database. Althogh there is no exception or error in the console(I've corretly configured log4j).
There is no an insert sql in the console and there is no data in the database.
It's strange, I can successfully use hibernate to get data from database and there are select sql in the console.
BTW.I used myeclipse reverse engeering to build the model and hbm file.
I tried several ways but failed:
It's not about transaction,I commit in TeamService.
I set log4j.properties to see all the errors ,but there is no exception.
I tried to assign the entity I need to save to a new instance, but still, no data in
I thought it is about composite-id in hbm ,but I don't know how to fix it .
Here's my Team entity(I eliminated the irrelevant part)
private TeamId id;
private Classes classes;
Here's my Team hbm
<hibernate-mapping>
<class name="edu.wzy.model.Team" table="team" catalog="test3" >
<composite-id name="id" class="edu.wzy.model.TeamId">
<key-property name="TNo" type="java.lang.Integer">
<column name="T_NO" />
</key-property>
<key-many-to-one name="SUser" class="edu.wzy.model.SUser">
<column name="S_ID" />
</key-many-to-one>
</composite-id>
<many-to-one name="classes" class="edu.wzy.model.Classes" fetch="select">
<column name="C_ID" not-null="true">
</column>
</many-to-one>
</class>
</hibernate-mapping>
Here's my TeamService
public class TeamServiceImpl implements TeamService
{
TeamDAO teamDAO=new TeamDAO();
public void save(Team t)
{
Session session=HibernateUtil.getCurrentSession();
session.getTransaction().begin();
teamDAO.save(t);
session.getTransaction().commit();
}
}
Here's my Dao
public void save(Team transientInstance)
{
try
{
getSession().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re)
{
log.error("save failed", re);
throw re;
}
}
Here's my action(I eliminated the irrelevant part)
public class TeamAction extends BaseAction implements ModelDriven<Team>
{
public String add()
{
Classes classes = classesService.findbyid(team.getClasses().getId());
Set s = team.getClasses().getStudents();
Iterator it = s.iterator();
while (it.hasNext())
{studentid = (String) it.next();}
Student student = studentService.findbyid(Integer.parseInt(studentid));
Team t=new Team();
t.setId(team.getId());
classes.getStudents().add(student);
t.setClasses(classes);
teamService.save(t);
return SUCCESS;
}
}
Try session.flush() before commit. Just like
public class TeamServiceImpl implements TeamService {
TeamDAO teamDAO = new TeamDAO();
public void save(Team t) {
Session session = HibernateUtil.getCurrentSession();
session.getTransaction().begin();
teamDAO.save(t);
session.flush();
session.clear();
session.getTransaction().commit();
session.close();
}
}
I have found where the problem is.
The problem is that the save method is not in the same session that TeamService provide.
I made a mistake.
The Dao classes are generated by machine, they extends a fathre class which has a method to generate session. But I also create a class HibernateUtil to generate session.
So save method is not in the same session as Service,which lead to failed saving.
The answer to this question is to modify the method in Dao.
public void save(Classes transientInstance)
{
log.debug("saving Classes instance");
try
{
HibernateUtil.getCurrentSession().save(transientInstance);
log.debug("save successful");
} catch (RuntimeException re)
{
log.error("save failed", re);
throw re;
}
}
I have spent literally two days on the problem and I'm no closer to solving it. I hope that one of the experts out there can help.
Here's the problem. I'm Using TomEE 1.6.0.2 (OpenEJB 4.6.0.2) and trying to do a simple TypedQyery that returns a single result and I am getting the strangest return value. It's not the bean object that I expect--it's a java.lang.Class!!
Here's the code fragment:
public static Account getAccount(String type, String key, EntityManager em) {
try {
TypedQuery<Account> query = em.createNamedQuery("Account.getByTypeAndKey", Account.class)
.setParameter("type", type)
.setParameter("key", key)
.setMaxResults(1);
Account account = query.getSingleResult(); // This statement throws an exception
}
catch (Exception e) {
System.out.println(e.getMessage()); // "java.lang.Class cannot be cast to beans.Account"
}
try {
TypedQuery<Account> query = em.createNamedQuery("Account.getByTypeAndKey", Account.class)
.setParameter("type", type)
.setParameter("key", key)
.setMaxResults(1);
Object object = query.getSingleResult(); // This works
System.out.printf("object is '%s'\n", object.toString());
// "object is 'class beans.Account'"
Account account = new Account();
System.out.printf("account is '%s'\n", account.toString());
// "account is 'Account{id=0, type='null, etc... }'"
return account;
}
catch (Exception e) {
System.out.println(e.getMessage()); // Never gets here
}
}
Here's a fragment of the Account class:
#Entity
#Table(name="Account")
#NamedQueries({
#NamedQuery(name = "Account.getByTypeAndKey", query = "SELECT Account FROM Account rec WHERE rec.key = :key AND rec.type = :type"),
})
public class Account implements Serializable {
private int id;
private String type;
// The usual stuff ...
}
And here is persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="unipagosPersistenceUnit" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>PAY_AccountDSJta</jta-data-source>
<non-jta-data-source>PAY_AccountDSNonJta</non-jta-data-source>
<class>beans.Account</class>
<properties>
<property name="openjpa.DynamicEnhancementAgent" value="true"/>
<property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
<property name="openjpa.Log" value="SQL=TRACE"/>
<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=72, PrintParameters=true, MaxActive=10, MaxIdle=5, MinIdle=2, MaxWait=60000"/>
</properties>
</persistence-unit>
</persistence>
I'm sure this is something really easy, but I'm not able figure this one out. What's going on?!
TIA for your help...
Your query is wrong. It should be
SELECT rec FROM Account rec WHERE rec.key = :key AND rec.type = :type
^-- use the alias here, and not the class name
Trying to understand more about Hibernate,I wrote some code which creates some entities and saves them in db and then tries to delete one of the entities.
The mapping file for entity Customer has id generator set to native.I am using postgresql as db.
...
<class name="Customer" table="CUSTOMER">
<id column="CUSTOMER_ID" name="customer_id" type="java.lang.Long">
<generator class="native"/>
</id>
...
I came across hibernate.NonUniqueObjectException.
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.me.hibernatestore.Customer#129]
Full stack trace here
I fired up the eclipse debugger and found that the object involved has same address in all the involved methods ..
The relevant part of the code is
public class Main {
CustomerDao custdao;
Customer mark;
public void storeDemo(){
custdao = DaoFactory.getCustomerDao();
createCustomers();
updateEntities();
deleteCustomer(mark);
}
private void createCustomers() {
mark = new Customer();
mark.setName("mark");
mark.setEmailAddress("mark#home");
mark.setAddress("121,3rd avenue");
mark.setCity("San Diego");
mark.setState("CA");
mark.setCountry("U.S.A");
}
private void updateEntities() {
Transaction tx = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
custdao.saveOrUpdateCustomer(mark);
tx.commit();
}catch(RuntimeException e){
tx.rollback();
throw e;
}
}
private void deleteCustomer(Customer cust){
Transaction tx = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
String custName = cust.getName();
custdao.deleteCustomer(cust);
tx.commit();
}catch(RuntimeException e){
tx.rollback();
throw e;
}
}
public static void main(String[] args) {
new Main().storeDemo();
}
}
With the help of debugger I found the address of object 'mark'
Main.createCustomers(): mark-> Customer#2bc3f5
CustomerDaoImpl.saveOrUpdateCustomer(Customer customer):customer-> Customer#2bc3f5
BaseDaoImpl.saveOrUpdate(T obj):obj-> Customer#2bc3f5
Main.deleteCustomer(Customer customer):customer-> Customer#2bc3f5
CustomerDaoImpl.deleteCustomer(Customer customer):customer-> Customer#2bc3f5
BaseDaoImpl.delete(T obj):obj-> Customer#2bc3f5
Experimenting further,I modified the code and through dao.findById() got a different object with same id and used that in deleteCustomer().This time the code worked without throwing any exception
public class Main {
CustomerDao custdao;
Customer mark;
public void storeDemo(){
custdao = DaoFactory.getCustomerDao();
createCustomers();
updateEntities();
Long mark_id = mark.getCustomer_id();
Customer mark2 = getCustomer(mark_id);
deleteCustomer(mark2);
}
private Customer getCustomer(Long id){
Transaction tx = null;
Customer cust = null;
Session session = HibernateUtil.getCurrentSession();
try{
tx = session.beginTransaction();
return custdao.findCustomerById(id);
}catch(RuntimeException e){
throw e;
}
}
...
}
Can someone explain this behaviour?My understanding about the 'a different object with the same identifier value' part of the error message is fuzzy ..The object as shown in debugger in the first case has same memory address everywhere in the code.Then how can it be a different object?
sincerely
Jim
This exception usually occurs when dealing with detached objects. In order to avoid that, you have to get the object and delete it in the same session or reattach it to the session and then delete it.
Hope this helps!
I got a really weird problem here and I absolutely cannot understand why this is happening.
The problem looks like this:
I got a class called "SmampiAccount" which holds a list of email accounts. The mapping file looks like this (shortened):
<hibernate-mapping>
<class name="com.smampi.web.model.account.SmampiAccount" table="SMAMPIACCOUNT">
<id name="id" type="long" access="field">
<column name="SMAMPI_ACCOUNT_ID" />
<generator class="native" />
</id>
<bag name="mailAccounts" table="MAILACCOUNTS" lazy="false" inverse="true">
<key column="SMAMPI_ACCOUNT_ID"></key>
<one-to-many class="com.smampi.web.model.mail.account.MailAccount"/>
</bag>
</class>
</hibernate-mapping>
I get instances of this class through this method:
public SmampiAccount loadSmampiAccount(long id) throws FailedDatabaseOperationException {
SmampiAccount smampiAccount = null;
Session session = null;
Transaction transaction = null;
try {
session = getSession();
transaction = session.beginTransaction();
smampiAccount = (SmampiAccount) session.load(com.smampi.web.model.account.SmampiAccount.class, id);
List<MailAccount> mailAccounts = smampiAccount.getMailAccounts();
doSomething(mailAccounts);
transaction.commit();
} catch (Exception e) {
rollback(transaction);
closeSession();
throw new FailedDatabaseOperationException(e);
} finally {
closeSession();
}
return smampiAccount;
}
private Session getSession() {
if (_session == null) {
_session = getSessionFactory().openSession();
}
if (_session.isOpen() == false) {
_session = getSessionFactory().openSession();
}
return _session;
}
This works fine as it is.
Now, I wanted to add a new property to the mapping file in order to save a reference to a default email account:
<many-to-one name="defaultMailAccount" column="DEFAULT_MAIL_ACCOUNT_ID" />
Now, I get an exception in the method public SmampiAccount loadSmampiAccount(long id) in this line:
List<MailAccount> mailAccounts = smampiAccount.getMailAccounts();
Stacktrace:
org.hibernate.SessionException: Session is closed!
at org.hibernate.impl.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:72)
at org.hibernate.impl.SessionImpl.getPersistenceContext(SessionImpl.java:1954)
at org.hibernate.event.def.DefaultPostLoadEventListener.onPostLoad(DefaultPostLoadEventListener.java:49)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:250)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982)
at org.hibernate.loader.Loader.doQuery(Loader.java:857)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
at org.hibernate.loader.Loader.loadEntity(Loader.java:2037)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:86)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:76)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3293)
at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:496)
at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:477)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:147)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
at org.hibernate.impl.SessionImpl.immediateLoad(SessionImpl.java:1026)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
at com.smampi.web.model.account.SmampiAccount_$$_javassist_19.getMailAccounts(SmampiAccount_$$_javassist_19.java)
How is this possible?
The session gets not closed manually and .commit() isn't called yet (which would normally close the session).
It's also not possible that another method is interfering here because I create a new hibernate session for each method call which is dedicated just for this one method.
Edit
I added some debug info on the session open status:
session = getSession();
System.err.println(session.isOpen());
transaction = session.beginTransaction(); // 1 (true)
System.err.println(session.isOpen()); // 2 (true)
smampiAccount = (SmampiAccount) session.load(com.smampi.web.model.account.SmampiAccount.class, id);
System.err.println(session.isOpen()); // 3 (true)
List<MailAccount> mailAccounts = smampiAccount.getMailAccounts(); // Throws exception that session is closed
doSomething(mailAccounts);
System.err.println(session.isOpen()); // 4 (not called)
transaction.commit();
This gives me:
true
true
true
org.hibernate.SessionException: Session is closed!
I am the biggest idiot in the world.
In the setter of defaultMailAccount, I had this:
public void setDefaultMailAccount(MailAccount defaultMailAccount) {
this.defaultMailAccount = defaultMailAccount;
try {
databasecontroller.update(this);
} catch (FailedDatabaseOperationException e) {
handleException(e, false, null, null);
}
}
The call to databasecontroller.update(this) caused a cascade whenever Hibernate tried to load a persisted version from the database and that again caused the session to close.
Moving the call of databasecontroller.update(..) to outside the method fixed the issue.
Sorry to everyone for taking up your time and thanks for the help!
I want to insert data into a table using the following code
public User registerUser(String usr, String pwd) {
u=em.find(User.class,usr);
if(u!=null)
{
return null;
}
String query1 = "insert into users values('" + usr + "','" + pwd +"')";
Query q = em.createQuery(query1);
u=em.find(User.class,usr);
return u;
}
here 'u' is the object of User class and em is EntityManager.
I get this following exception:
Servlet.service() for servlet action threw exception
org.hibernate.hql.ast.QuerySyntaxException: expecting OPEN, found 'values' near line 1, column 19 [insert into users values('pawan','am')]
Try
public User registerUser(String usr, String pwd) {
u=em.find(User.class,usr);
if(u!=null)
{
return null;
}
//Now saving...
em.getTransaction().begin();
em.persist(u); //em.merge(u); for updates
em.getTransaction().commit();
em.close();
return u;
}
If the PK is Identity, it will be set automatically in your persisted class, if you are using auto generation strategy (thanks to David Victor).
Edit to #aman_novice comment:
set it in your class
//Do this BEFORE getTransaction/persist/commit
//Set names are just a example, change it to your class setters
u.setUsr(usr);
u.setPwd(pwd);
//Now you can persist or merge it, as i said in the first example
em.getTransaction().begin();
(...)
About #David Victor, sorry I forgot about that.
You're not using SQL but JPAQL, there is no field-based insert. You persist object rather than inserting rows.
You should do something like this:
public User registerUser(String usr, String pwd) {
u=em.find(User.class,usr);
if(u!=null)
{
return u;
}
u = new User(usr, pwd);
em.persist(u);
return u;
}
This isn't really the way to go. You are trying to insert a row in a table but have no associated attached entity. If you're using the JPA entity manager - then create a new instance - set the properties & persist the entity.
E.g.
User u = new User();
u.setXXX(xx);
em.persist(u);
// em.flush() <<-- Not required, useful for seeing what is happening
// etc..
If you enable SQL loggging in Hibernate & flush the entity then you'll see what is sent to the database.
E.g. in persistence.xml:
<property name="hibernate.format_sql" value="true" />