I have a simple heirarcy that won't work. It is pretty much the Category of caveat emptor:
Code:
[Category]<>-- {0..1} +parent +child {0..*} --[Category]
Popuplate a tree using local src directory:
Code:
alter table Category drop constraint FK6DD211EBB470874
alter table Category drop constraint FK1_parentEE
drop table Category if exists
create table Category (Category_PK bigint generated by default as identity (start with 1), name varchar(255), parent_FK bigint, primary key (Category_PK))
alter table Category add constraint FK6DD211EBB470874 foreign key (Category_PK) references Category
alter table Category add constraint FK1_parentEE foreign key (parent_FK) references Category
5186 [main] INFO hbm2ddl.SchemaExport - schema export complete
5190 [main] INFO connection.DriverManagerConnectionProvider - cleaning up connection pool: jdbc:hsqldb:hsql://localhost/xdb
src
se
snigel
testcase
Process finished with exit code 0
It prints the heirarchy right, so my getters and setters work.
This is what it looks like in the database:
Code:
PK NAME PARENT
1 src <null>
2 se 1
3 snigel 2
4 testcase 3
Then i start a new jvm, loading the root heirarchy and print it out:
Code:
src
src
src
src
src
src
src
src
src
src
src
Exception in thread "main" java.lang.RuntimeException: too many levels
I get the category, but it only contains one child, it self.
Eternal loop.
Hibernate version: 3.1 beta 2
Name and version of the database you are using:HSQLdb 1.7.3.3
Mapping documents:se.snigel.testcase:Collection.hbm.xml
Code:
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class
abstract="false"
dynamic-insert="true"
dynamic-update="true"
lazy="true"
name="se.snigel.testcase.Category"
table="Category"
>
<id
name="primaryKey"
column="Category_PK"
type="long"
unsaved-value="null"
>
<generator class="native"/>
</id>
<property
column="name"
name="name"
not-null="false"
type="string"
/>
<many-to-one
name="parent"
cascade="all"
class="se.snigel.testcase.Category"
foreign-key="FK1_parentEE"
access="property">
<column
name="parent_FK"
not-null="false"
/>
</many-to-one>
<set
access="property"
lazy="true"
cascade="all"
inverse="true"
name="children"
>
<key column="Category_PK"/>
<one-to-many
class="se.snigel.testcase.Category"
/>
</set>
</class>
</hibernate-mapping>
se.snigel.testcase:HibernateUtil.java
Code:
/*
* Created on Apr 21, 2005
*/
package se.snigel.testcase;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.Comparator;
//*/
/**
* Basic Hibernate helper class, handles SessionFactory, Session and
* Transaction.
* <p/>
* Uses a static initializer for the initial SessionFactory creation and holds
* Session and Transactions in thread local variables. All exceptions are
* wrapped in an unchecked RuntimeException.
*
* @author christian@hibernate.org
* @author Karl Wettin <kalle@snigel.net>
*/
public class HibernateUtil {
/**
* The MyEchelon instance place holder.
*/
private static Long myEchelonPrimaryKey;
public static String jdbcDriver = "org.hsqldb.jdbcDriver";
public static String jdbcURL = "jdbc:hsqldb:hsql://localhost/xdb";
public static String jdbcUser = "sa";
public static String jdbcPassword = "";
public static String hibernateSQLDialect = "org.hibernate.dialect.HSQLDialect";
/*
public static String jdbcDriver = "com.mysql.jdbc.Driver";
public static String jdbcURL = "jdbc:mysql://localhost:3306/myechelon?autoReconnect=true";
public static String jdbcUser = "root";
public static String jdbcPassword = "";
public static String hibernateSQLDialect = "org.hibernate.dialect.MySQLDialect";
*/
private static Logger log = LogManager.getLogger(HibernateUtil.class);
private static Configuration configuration;
private static SessionFactory sessionFactory;
private static final ThreadLocal threadSession = new ThreadLocal();
private static final ThreadLocal threadTransaction = new ThreadLocal();
private static final ThreadLocal threadInterceptor = new ThreadLocal();
// Create the initial SessionFactory from the default configuration files
static {
try {
configuration = new Configuration()
.addClass(Category.class);
try {
Class.forName(jdbcDriver).newInstance();
} catch (Exception ex) {
ex.printStackTrace();
}
configuration.setProperty("hibernate.connection.url", jdbcURL);
if (jdbcUser != null) {
configuration.setProperty("hibernate.connection.user", jdbcUser);
}
if (jdbcPassword != null) {
configuration.setProperty("hibernate.connection.password", jdbcPassword);
}
if (hibernateSQLDialect != null) {
configuration.setProperty("hibernate.dialect", hibernateSQLDialect);
}
configuration.setProperty("hibernate.show_sql", "false");
configuration.setProperty("hibernate.connection.autocommit", "false");
configuration.setProperty("hibernate.query.substitutions", "true 1, false 0, yes 'Y', no 'N'");
configuration.setProperty("hibernate.connection.pool_size", "1");
configuration.setProperty("hibernate.proxool.pool_alias", "pool1");
configuration.setProperty("hibernate.jdbc.batch_size", "0");
configuration.setProperty("hibernate.jdbc.batch_versioned_data", "true");
configuration.setProperty("hibernate.jdbc.use_streams_for_binary", "true");
configuration.setProperty("hibernate.max_fetch_depth", "1");
configuration.setProperty("hibernate.cache.region_prefix", "hibernate.test");
configuration.setProperty("hibernate.cache.use_query_cache", "true");
configuration.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.EhCacheProvider");
configuration.setProperty("hibernate.proxool.pool_alias", "pool1");
configuration.setProperty("hibernate.proxool.pool_alias", "pool1");
sessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
// We have to catch Throwable, otherwise we will miss
// NoClassDefFoundError and other subclasses of Error
log.error("Building SessionFactory failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
* Returns the SessionFactory used for this static class.
*
* @return SessionFactory
*/
public static SessionFactory getSessionFactory() {
/*
* Instead of a static variable, use JNDI: SessionFactory sessions =
* null; try { Context ctx = new InitialContext(); String jndiName =
* "java:hibernate/HibernateFactory"; sessions =
* (SessionFactory)ctx.lookup(jndiName); } catch (NamingException ex) {
* throw new RuntimeException(ex); } return sessions;
*/
return sessionFactory;
}
/**
* Returns the original Hibernate configuration.
*
* @return Configuration
*/
public static Configuration getConfiguration() {
return configuration;
}
/**
* Rebuild the SessionFactory with the static Configuration.
*/
public static void rebuildSessionFactory() throws RuntimeException {
synchronized (sessionFactory) {
try {
sessionFactory = getConfiguration().buildSessionFactory();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
/**
* Rebuild the SessionFactory with the given Hibernate Configuration.
*
* @param cfg
*/
public static void rebuildSessionFactory(Configuration cfg) throws RuntimeException {
synchronized (sessionFactory) {
try {
sessionFactory = cfg.buildSessionFactory();
configuration = cfg;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
/**
* Retrieves the current Session local to the thread. <p/>If no Session is
* open, opens a new Session for the running thread.
*
* @return Session
*/
public static Session getSession() throws RuntimeException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
if (log.isDebugEnabled()) {
log.debug("Opening new Session for this thread.");
}
if (getInterceptor() != null) {
if (log.isDebugEnabled()) {
log.debug("Using interceptor: " + getInterceptor().getClass());
}
s = getSessionFactory().openSession(getInterceptor());
} else {
s = getSessionFactory().openSession();
}
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new RuntimeException(ex);
}
return s;
}
/**
* Closes the Session local to the thread.
*/
public static void closeSession() throws RuntimeException {
try {
Session s = (Session) threadSession.get();
threadSession.set(null);
if (s != null && s.isOpen()) {
if (log.isDebugEnabled()) {
log.debug("Closing Session of this thread.");
}
s.close();
}
} catch (HibernateException ex) {
throw new RuntimeException(ex);
}
}
/**
* Start a new database transaction.
*/
public static void beginTransaction() throws RuntimeException {
Transaction tx = (Transaction) threadTransaction.get();
try {
if (tx == null) {
if (log.isDebugEnabled()) {
log.debug("Starting new database transaction in this thread.");
}
tx = getSession().beginTransaction();
threadTransaction.set(tx);
}
} catch (HibernateException ex) {
throw new RuntimeException(ex);
}
}
/**
* Commit the database transaction.
*/
public static void commitTransaction() throws RuntimeException {
Transaction tx = (Transaction) threadTransaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
tx.commit();
if (log.isDebugEnabled()) {
log.debug("Committing database transaction of this thread.");
}
}
threadTransaction.set(null);
} catch (HibernateException ex) {
rollbackTransaction();
throw new RuntimeException(ex);
}
}
/**
* Commit the database transaction.
*/
public static void rollbackTransaction() throws RuntimeException {
Transaction tx = (Transaction) threadTransaction.get();
try {
threadTransaction.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
if (log.isDebugEnabled()) {
log.debug("Tyring to rollback database transaction of this thread.");
}
tx.rollback();
}
} catch (HibernateException ex) {
throw new RuntimeException(ex);
} finally {
closeSession();
}
}
/**
* Reconnects a Hibernate Session to the current Thread.
*
* @param session The Hibernate Session to be reconnected.
*/
public static void reconnect(Session session) throws RuntimeException {
try {
session.reconnect();
threadSession.set(session);
} catch (HibernateException ex) {
throw new RuntimeException(ex);
}
}
/**
* Disconnect and return Session from current Thread.
*
* @return Session the disconnected Session
*/
public static Session disconnectSession() throws RuntimeException {
Session session = getSession();
try {
threadSession.set(null);
if (session.isConnected() && session.isOpen())
session.disconnect();
} catch (HibernateException ex) {
throw new RuntimeException(ex);
}
return session;
}
/**
* Register a Hibernate interceptor with the current thread.
* <p/>
* Every Session opened is opened with this interceptor after registration.
* Has no effect if the current Session of the thread is already open,
* effective on next close()/getSession().
*/
public static void registerInterceptor(Interceptor interceptor) {
threadInterceptor.set(interceptor);
}
private static Interceptor getInterceptor() {
Interceptor interceptor = (Interceptor) threadInterceptor.get();
return interceptor;
}
}
se.snigel.testcase:Category.java
Code:
package se.snigel.testcase;
public class Category {
private java.lang.String name;
private java.lang.Long primaryKey = null;
/**
* [Category]-- {0..*} +child +parent {0..1} --< >[Category]
*
* Attribute holder of association 'parent' in this class.
*/
private se.snigel.testcase.Category parent;
/**
* [Category]< >-- {0..1} +parent +child {0..*} --[Category]
*
* Attribute holder of association 'children' in this class.
*/
private java.util.Set children = new java.util.HashSet();
/**
* @return java.lang.Long
*/
public java.lang.Long getPrimaryKey() {
return this.primaryKey;
}
/**
* @param primaryKey
*/
public void setPrimaryKey(java.lang.Long primaryKey) {
this.primaryKey = primaryKey;
}
/**
* @return java.lang.String
*/
public java.lang.String getName() {
return this.name;
}
/**
* @param name The name to be set.
*/
public void setName(java.lang.String name) {
this.name = name;
}
/**
* @return se.snigel.testcase.Category
*/
public se.snigel.testcase.Category getParent() {
return this.parent;
}
/**
* @param parent
*/
public void setParent(se.snigel.testcase.Category parent) {
this.parent = parent;
}
/**
* @return java.util.Set
*/
public java.util.Set getChildren() {
return this.children;
}
/**
* @param children
*/
public void setChildren(java.util.Set children) {
this.children = children;
}
/**
* @param o
* @return boolean
*/
public boolean equals(java.lang.Object o) {
if (o == null) {
return false;
}
if (o == this) {
return true;
}
if (this.getPrimaryKey() == null) {
return false;
}
// will fail in most cases, since hibernate use cglib to generate new inner subclasses. if (!o.getClass().equals(this.getClass())) return false;
if (!(o instanceof se.snigel.testcase.Category)) {
return false;
}
return this.getPrimaryKey().equals(((se.snigel.testcase.Category) o).getPrimaryKey());
}
/**
* @return int
*/
public int hashCode() {
if (getPrimaryKey() != null) {
return getPrimaryKey().hashCode();
} else {
return super.hashCode();
}
}
}
se.snigel.testcase:Main.java
Code:
package se.snigel.testcase;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import java.io.File;
import java.io.FileFilter;
import java.util.Iterator;
/**
* User: kalle
* Date: 2005-nov-20
* Time: 23:44:08
*/
public class Main {
public static final void main(String[] args) throws Exception {
int FIRST_RUN = 0;
int SECOND_RUN = 1;
int run = 0;
if (run == FIRST_RUN) {
new SchemaExport(HibernateUtil.getConfiguration()).create(true, true);
}
HibernateUtil.beginTransaction();
Category rootCategory;
if (run == FIRST_RUN) {
File rootDir = new File("src");
rootCategory = new Category();
rootCategory.setName("root");
create(rootDir, rootCategory);
HibernateUtil.commitTransaction();
}
rootCategory = (Category) HibernateUtil.getSession().createQuery("from Category c where c.id = 1").list().get(0);
print(rootCategory, 0);
}
private static void print(Category category, int level) {
if (level > 10) {
throw new RuntimeException("too many levels");
}
for (int i = 0; i < level; i++) {
System.out.print('\t');
}
System.out.println(category.getName());
for (Iterator it = category.getChildren().iterator(); it.hasNext();) {
print((Category) it.next(), level + 1);
}
}
private static void create(File dir, Category category) {
category.setName(dir.getName());
File[] subdirs = dir.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory();
}
});
for (File subdir : subdirs) {
Category child = new Category();
child.setParent(category);
category.getChildren().add(child);
create(subdir, child);
}
HibernateUtil.getSession().save(category);
}
}