I'm working on a project which is an assignment in a course I'm taking in the university.
In this project we are to implement an online forum system in a few iterations.
One of the requirements in this iteration is using ORM - we chose Hibernate and it's the first time we are experiencing it. so far we had a rough time :)
We're using intelliJ Idea 14, windows, java 7, mysql 5 and hibernate 4.2.
Among many classes we have POJOs that represent the domain layer and we have hibernateUtils which encapsulates the access to the DB, And a Junit class for testing it.
These are the annotated POJOs:
User:
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue
#Column(name="user_id")
private int id;
#Column(name="name")
private String username;
#Column(name="email_address")
private String emailAddress;
#Column(name="password")
private String hashedPassword;
#Column(name="date")
#Temporal(TemporalType.DATE)
private java.util.Date creationDate;
#OneToOne
#JoinColumn(name="state")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private UserState state;
#Column(name="is_active")
private boolean active;
#Column(name="is_banned")
private boolean banned;
#Column(name="is_logged_in")
private boolean loggedIn;
#ManyToMany
#Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
private Set<User> friends;
#OneToMany
#JoinColumn(name="panding_notfications")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<Notification> pendingNotifications;
#OneToMany( mappedBy = "receivingMember")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<FriendRequest> friendRequests;
#OneToMany( mappedBy = "reporter")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<Report> sentReports;
#Column(name="seniority")
private int seniority;
#Column(name="loginTime")
private long loginTime;
Forum:
#Entity
#Table(name="Forum")
public class Forum {
#Id
#GeneratedValue
#Column(name="forum_id", nullable = false, unique = true)
public int id;
#Column(name="name")
private String name;
#OneToOne
#JoinColumn(name = "admin")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private User admin;
#OneToMany
#JoinColumn(name="sub_forums")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<SubForum> subForums;
#OneToMany
#JoinColumn(name="members")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<User> members;
#OneToOne
#JoinColumn(name="policy")
#Cascade({org.hibernate.annotations.CascadeType.ALL})
private ForumPolicy policy;
HibernateUtils:
public class HibernateUtils {
private static Configuration cfg;
private static SessionFactory sessionFactory =null;
public static boolean configure(boolean init) {
cfg = new Configuration()
.addAnnotatedClass(Forum.class)
.addAnnotatedClass(Message.class)
.addAnnotatedClass(SubForum.class)
.addAnnotatedClass(Thread.class)
.addAnnotatedClass(ForumPolicy.class)
.addAnnotatedClass(User.class)
.addAnnotatedClass(Report.class)
.addAnnotatedClass(Notification.class)
.addAnnotatedClass(FriendRequest.class)
.addAnnotatedClass(UserState.class)
.addAnnotatedClass(UserStatusPolicy.class)
.setProperty("hibernate.current_session_context_class","thread")
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect")
.setProperty("hibernate.connection.driver_class","com.mysql.jdbc.Driver")
.setProperty("hibernate.connection.url","jdbc:mysql://localhost:3306/forum_system")
.setProperty("hibernate.connection.username","root")
.setProperty("hibernate.password", "");
if (init) {
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
}
sessionFactory = cfg.configure().buildSessionFactory();
return cfg!=null;
}
public synchronized static boolean start() {
return configure(false);
}
public static boolean init() {
return configure(true);
}
public synchronized static boolean save(Object o) {
return saveReturnId(o) > 0;
}
public synchronized static int saveReturnId(Object o) {
Session session = sessionFactory.getCurrentSession();
try {
session.getTransaction().begin();
int id= (int)session.save(o);
session.getTransaction().commit();
return id;
}
catch(HibernateException e) {
System.out.println(e);
ForumLogger.errorLog(e.toString());
return -1;
}
finally{
if(session.isOpen()) {
session.close();
}
}
}
public synchronized static boolean del(Object o) {
Session session = sessionFactory.getCurrentSession();
try {
session.getTransaction().begin();
session.delete(o);
session.getTransaction().commit();
return true;
}
catch(HibernateException e) {
System.out.println(e);
ForumLogger.errorLog(e.toString());
return false;
}
finally{
if(session.isOpen()) {
session.close();
}
}
}
public synchronized static Object load(Class c,int id) {
Object o=null;
Session session = sessionFactory.getCurrentSession();
try {
session.getTransaction().begin();
o=session.get(c, id);
session.getTransaction().commit();
return o;
}
catch(HibernateException e) {
System.out.println(e);
ForumLogger.errorLog(e.toString());
return null;
}
finally{
if(session.isOpen()) {
session.close();
}
}
}
}
The part of the test class that we are currently running:
public class ForumTests {
#BeforeClass
public static void Setup() {
HibernateUtils.start();
}
#Test
public void testSaveForum() {
Forum f = new Forum(new User(),new ForumPolicy(),"forum_name");
int id = HibernateUtils.saveReturnId(f);
Assert.assertEquals(f, (Forum) HibernateUtils.load(Forum.class, id));
}
#Test
public void testGetAdmin() {
User u =User.newMember("asdsad","asd","sdkfdjk#sldkf.com");
Forum f = new Forum(u,new ForumPolicy(),"forum_name");
int fid = HibernateUtils.saveReturnId(f);
Forum ormf = (Forum) HibernateUtils.load(Forum.class, fid);
Assert.assertEquals(u, ormf.getAdmin());
}
#Test
public void testAddSubforumFromForum() {
User u =User.newMember("user","pass","mail#gmail.com");
int uid = (int)HibernateUtils.saveReturnId(u);
Assert.assertTrue(uid>0);
/* commented out code */
}
When running each of the following tests separately they all succeed. But when running all of them we get this error: (In testGetAdmin test)
May 27, 2015 2:11:43 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 1452, SQLState: 23000
May 27, 2015 2:11:43 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Cannot add or update a child row: a foreign key constraint fails (`forum_system`.`forum`, CONSTRAINT `FK_l9pbqj2i36itiuhbu7q61xqlq` FOREIGN KEY (`admin`) REFERENCES `user` (`user_id`))
org.hibernate.exception.ConstraintViolationException: could not execute statement
which I pretty much understand but i can't figure out why should that happen only when running all tests together? (maybe something about transactions?)
Thanks :)
Your tests are stateful, i.e. they are changing the data and leaving it changed after they run.
That means that when the next test runs, the data isn't "clean" but has been poluted by the tests before it.
To fix this, make sure the individual tests clean up after themselves OR set up a clean environment before each test runs - At present you initialise hibernate once for the whole class
Related
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 :)
This question already has an answer here:
Failed to lazily initialize a collection, no session or session was closed (despite eagerly fetching)
(1 answer)
Closed 9 years ago.
code
pin of l_details has foreign constraint which refers to x_details primary key here i use unidirectional relationship from XDetails class.
Entities:
#Entity
#Table(name = "x_details")
public class XDetails implements Lifecycle {
private int y;
....
.....
private Set<LDetails> lDetails = new HashSet<LDetails>(0);
.........
..........
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="pin")
public Set<LDetails> getLDetails() {
return this.lDetails;
}
#Override
public void onLoad(Session s, Serializable id) {
System.out.println("inside load");
try{
if((lDetails!=null)){
System.out.println("size of lDetails"+lDetails.size());
}
}
catch(Exception exception){
exception.printStackTrace();
}
}
}
#Entity
#Table(name = "l_details")
public class LDetails {
private Integer id1;
private Integer pin;
....
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id1", unique = true, nullable = false)
public Integer getId1() {
return this.id1;
}
public void setId1(Integer id1) {
this.id1 = id1;
}
#Column(name="pin")
public Integer getPin() {
return this.pin;
}
public void setPin(Integer pin) {
this.pin = pin;
}
}
Main thread:
Session session=HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Criteria criteria=session.createCriteria(XDetails.class);
criteria.createAlias("lDetails", "status",CriteriaSpecification.INNER_JOIN);
criteria.setFetchMode("status", FetchMode.JOIN);
List<XDetails> details=criteria.list();
session.getTransaction().commit();
Exception (inside onload ):
org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
at rg.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122)
at org.hibernate.collection.PersistentSet.size(PersistentSet.java:162)
at com.hibernate.test.pendingdict3.XDetails.onLoad(XDetails.java:588)
at org.hibernate.event.def.DefaultPostLoadEventListener.onPostLoad(DefaultPostLoadEventListener.java:73)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:234)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:898)
at org.hibernate.loader.Loader.doQuery(Loader.java:773)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:270)
at org.hibernate.loader.Loader.doList(Loader.java:2449)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2192)
at org.hibernate.loader.Loader.list(Loader.java:2187)
at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:119)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1706)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
at com.hibernate.test.main4.HibernateStandaloneMain.main(HibernateStandaloneMain.java:24)
I am using Hibernate 3.5.6 final dependency, mysql database and I am running hibernatestandalone application.
# onload method
#Override
public void onLoad(Session s, Serializable id) {
System.out.println("inside load");
try{
if((lDetails!=null)){
System.out.println("size of lDetails"+lDetails.size());
}
}
catch(Exception exception){
exception.printStackTrace();
}
}
The problem is that you choose LAZY loading but your session is already closed when you first access the elements of your collection. You need to EAGER fetch your Set<LDetails> or keep your session open until all operations are done.
So I keep coming up short on this, and just can't seem to get it to work right.
Things seem to work fine when I use my HibernateUtil.get(clazz, pkId) method, but when I try using the HibernateUtil.pagedQuery(clazz, criterion, start, stop) I get multiple identical objects returned.
For example if there were 3 employees assigned to Role 1, then running...
Role role = HibernateUtil.get(Role.class, new Integer(1));
... works as expected. However, if I run...
List<Criterion> c = new ArrayList();
c.add(Restrictions.eq("roleTypeSeqNo", new Integer(1)));
List<Role> roles = (List<Role>) phi.util.hibernate.HibernateUtil.pagedQuery(Role.class, c, 0, 50);
... returns a List of 3 identical roles. All of which represent Role 1.
If someone could guide me down the right path, I would really appreciate it.
Thanks in advance!
Here's an abbreviated version of my classes
#Entity
#Table(name="ASSIGNMENTS")
public class Assignment implements Serializable {
#EmbeddedId
private AssignmentPK pk;
// After coming across many errors I finally caved and reverted roleTypeSeqNo back to just an Integer.
private Integer roleTypeSeqNo;
private String status;
private String location;
}
#Embeddable
public class AssignmentPK implements Serializable {
#ManyToOne
#JoinColumn(name="EMPLID")
private Employee employee;
#Column(name="PRIORITY_NO")
private String priorityNo;
}
#Entity
public class Employee implements Serializable {
#Id
private Integer emplId;
private String name;
}
#Entity
public class Role implements Serializable {
#Id
private Integer roleTypeSeqNo;
private Integer reportsToRole;
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="roleTypeSeqNo")
#JoinTable(
name="ASSIGNMENTS"
, joinColumns={#JoinColumn(name="ROLE_TYPE_SEQ_NO")}
, inverseJoinColumns={
#JoinColumn(name="EMPLID"),
#JoinColumn(name="PRIORITY_NO")
}
)
private List<Assignment> assignments;
}
public class HibernateUtil {
public static Object get(Class clazz, Serializable pkId) {
Session session = getSession();
Transaction transaction = session.beginTransaction();
Object obj = session.get(clazz, pkId);
transaction.commit();
session.close();
return obj;
}
public static List pagedQuery(Class clazz, List<Criterion> criterion, Integer start, Integer size){
Session session = getSession();
try {
Transaction transaction = session.beginTransaction();
DetachedCriteria dCriteria = DetachedCriteria.forClass(clazz);
for(Criterion c : criterion){
dCriteria.add(c);
}
dCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
dCriteria.setProjection(Projections.id());
Criteria criteria=session.createCriteria(clazz);
criteria.add(Subqueries.propertyIn("id", dCriteria));
criteria.setFirstResult(start);
criteria.setMaxResults(size);
List records = criteria.list();
transaction.commit();
return records;
} catch (Exception e) {
Logger.getLogger("HibernateUtil").log(Level.SEVERE, "There was an EXCEPTION THROWN!!!", e);
return null;
} finally {
session.close();
}
}
}
dCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
should be on the main criteria
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
Also there is no need for the subquery there. The following is enough
Criteria criteria = session.createCriteria(clazz);
for(Criterion c : criterions){
criteria.add(c);
}
criteria.setFirstResult(start);
criteria.setMaxResults(size);
List records = criteria.list();
I have the following class:
#Entity
#Table(name="tbl_books")
public class Book{
private int id_book;
private String isbn;
private Set<Author> authors;
#ManyToMany(cascade=CascadeType.ALL, fetch= FetchType.EAGER)
#JoinTable(name="tbl_books_tbl_authors",
joinColumns={#JoinColumn(name="id_book")},
inverseJoinColumns= {#JoinColumn(name="id_author")})
public Set<Author> getAuthors() {
return authors;
}
public void setAuthors(Set<Author> authors) {
this.authors = authors;
}
This is the function in BookController for updating the book info:
#RequestMapping(method=RequestMethod.POST)
public String dataBook(#Valid BookBean bb, BindingResult result){
if (result.hasErrors()){
return "redirect:/books/formBook";
}
booksService.saveBook(bb);
return "redirect:/books";
}
In booksService I have this one:
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void saveBook(BookBean lb) {
DataParser dp = new DataParser();
booksDAO.sBook(dp.fromBookBeanToBook(lb));
}
In booksDAO:
#Override
public void sBook(Book book) {
sessionFactory.getCurrentSession().saveOrUpdate(book);
}
Then, when I try to update a Book from that form Hibertate does this:
Hibernate: update tbl_books set date=?, isbn=? where id_book=?
Hibernate: delete from tbl_books_tbl_authors where id_book=?
Why does Hibernate do this and how can I fix it?
Ok, got it, when trying to update the Authors Set was empty, so Hibernate did a delete instead of update.
i have write the criteria for company class.
below are company class, companySearch class and criteria. But criteria list is throw exception. exception is "org.hibernate.QueryException: could not resolve property: san.san of: com.sesami.common.domain.Company". How to access Company.san.san?
Company class
public class Company extends DomainObject implements UserDetails {
private Long id;
private String companyName;
private CompanyType companyType;
private String description;
private String companyURL;
private String billToEmail;
private String hashPassword;
private SAN san;
#OneToOne(cascade = { CascadeType.ALL })
public SAN getSan() {
return san;
}
public void setSan(SAN san) {
this.san = san;
}
...
}
CompanySearch
public class CompanySearch {
private String companyName;
private String email;
private Long san;
private String gstNumber;
......
public Long getSan() {
return san;
}
public void setSan(Long san) {
this.san = san;
}
...
}
Criteria
companyCriteria = this.getSession().createCriteria(
Company.class);
if (companySearch.getSan() != null
&& !"".equals(companySearch.getSan()))
companyCriteria.add(Restrictions.eq("san.san",
companySearch.getSan()));
Integer count = ((Long) companyCriteria.setProjection(
Projections.rowCount()).uniqueResult()).intValue();
companyCriteria.setProjection(null);
companyCriteria.setResultTransformer(Criteria.ROOT_ENTITY);
companyCriteria
.setFirstResult((pager.getPage() - 1) * pager.getPageSize())
.setMaxResults(pager.getPageSize()).list();
List<Company> companies = companyCriteria.list();
PagedResultSet pr = new PagedResultSet();
pr.setPager(pager);
pr.setResultSet(companies);
pr.setRowCount(count);
return pr;
You must create a join to the San entity, using a subcriteria, or an alias:
companyCriteria.createAlias("san", "sanAlias");
companyCriteria.add(Restrictions.eq("sanAlias.san",
companySearch.getSan()));
or
companyCriteria.createCriteria("san").add(Restrictions.eq("san",
companySearch.getSan()));
This is well explained in the Hibernate reference documentation and even in the Criteria javadoc.
Note that this has absolutely nothing to do with Spring, and everything to do with Hibernate. If you searched in the Spring doc for how to do this, no wonder you didn't find anything.