显示标签为“Hibernate”的博文。显示所有博文
显示标签为“Hibernate”的博文。显示所有博文

2009年5月12日星期二

ORM Relationships

DataNucleus的文档不错,写得很精要,可以读一下:Relationships

在ORM的表述中经常用到 Entity这个词,entity对应于OO的class,对应于DB的Table。

relationship: 1-1, 1-N, N-1, N-M,N-M是bidirectional的,其它的都分为unidirectinal 或者bidirectional。

各种关系中的life dependence是不同的,用GAE的JPA文档中的话来说就是owned和unowned关系。用UML的术语就是: aggregation和composition关系。聚集和合成是很强的耦合关系,在GAE中就是owned关系。在hibernate 中,这种关系就可以用来表达。如果用来表达,那么CascadeType就要进行合适的设置来表达owned或是unowned。CascadeType参数是跟对象生命期有关的。

one-to-one:
1)两个Entity共享主键。
2) Foreign Key。

Assigning Relationships:
With a bidirectional relation you must set both sides of the relation 。

Persisting Relationships - Reachability:
persistence-by-reachability。

Managed Relationships:

Casecading:
casecading要解决的问题是,你对一个object进行持久化操作时,它的域对象是否也执行相应的持久化操作。DataNucleus的casecading例子中描述的情景不错。一个“驾驶员”有一个“驾驶执照”,同时还拥有多辆“汽车”。当驾驶员不存在了,执照也跟随被删除,但是汽车不该被删除。如果汽车被删除,那么驾驶员应该还存在。这是符合现实常理的。用casecading的annotation解决此问题:
@OneToOne(cascade=CascadeType.ALL)
@OneToMany(mappedBy="owner", cascade={CascadeType.PERSIST, CascadeType.MERGE})
@ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})

想建立coder到project的many-to-many映射,但是GAE server却总是报错,才想起GAE对JPA中的
“多对多”是不支持的。需要在各个类中添加Key的集合,来模拟多对多。

2009年2月26日星期四

Hibernate 的Configuration和它的ClassLoader.

还是在整理遗留系统的hibernate问题。
现状:
1)应用是eclipse RCP应用。多个自定义的plugin。其中一个是用于共享通用代码目的的base plugin。
2)Hibernte2.0版。在base plugin项目中包含了一个hibernte.cfg.xml,在其lib目录下有hibernate2.jar。其它plugin的lib目录也有hibnerate2.jar,也在使用自己的hibernate.cfg.xml.

问题:
1)开始试图让整个应用只有一个sessionFactory实例。发现这就有个项目依赖问题。如果把hibernate sessionFactory的管理放在base-plugin项目里。那么其它项目如果需要自己的hibernate dao 访问怎么办?虽然可以通过在base-plugin模块里定义一些配置文件读取点的方式加载额外的hibernate mapping文件,但是,如何把子项目的class加到base-plugin的classpath中呢?(也许可以写自己的classloader)。通过配置文件是解决依赖的一个好办法。但是,没有写classloader的经验,也没有时间去做这种尝试。这种逻辑放在框架里比较合适,而不是放在仅仅作为工具库的base project里。

2)接下来就考虑把Hibernate的jar文件只放在base-plugin的lib中。如果那个项目需要书库访问功能,就在自己的(子)项目中写new Configuration().configure().buildSessionFactory()。觉得这还算个差不多干净的办法。当进行代码实现时,技术性问题又冒出来了。以下蓝色文字仅代表自己对所遇问题的一些理解和猜测。plugin运行时有自己的classloader。而hibernte的Congfiguration().configure()在搜索hibernate.cfg.xml时是从加载Configuration这个类的classloader的classpath路径中寻找。这在hibernate2.jar仅存在于base项目的情况下是找不到你子项目的hibernate配置的,它又重复加载了base项目中的hibernate配置。查看Hibernat API文档,知道可以给configure()传入resource参数指定hibernate.cfg.xml的位置。通过AbcClass.class.getClassLoader().getResource("/abcHibernate.cfg.xml");的方式指定了abc项目用的.cfg.xml文件。Ok. Hibernate 被成功初始化了。但是,当使用hibernate query后,访问list的元素时发生了ClassCaseException,跟踪的SDK的source, List还是跟classloader有关系的。

3)接下来的想法就是把hibernate的jar文件放在各自project的lib里。应用程序在启动时,启动一个线程初始化sesssionFactory(),问题又来了。当第二个sessionFactory在同一个线程中初始化时,得到了另一个运行时 exception: Exception in thread "Thread-2" java.lang.LinkageError: Class a/b/c/d/Abc.class violates loader constraints. ... ... google到这段。没办法了。就先初始化一个sessionFactory吧。只是不知道Hibernte3.0是否也是同样的机制。
Hibernate always uses the classloader of the current running thread, which guarantees predictable behavior in all environments. In some environments with weak classloader/deployment configuration options (SAP, JONAS, etc.), it is necessary to tell Hibernate to use a different classloader, not the loader that loaded hibernate2.jar. Usually, this should be transparent to Hibernate and the application server would know what classloaders to use, depending on the packaging. Also note that this is not an issue if the "one application - one Hibernate version" strategy is used, which is the case in most production environments (you have dedicated libraries per application).

We currently allow a customizable classloader for addResource(), but this only loads the mappings with the given classloader. We would need an option to specify a classloader for the persistent classes as well, basically, for all Hibernate operations.
http://opensource.atlassian.com/projects/hibernate/browse/HB-1310
经验:
是不是common的功能,有时候不能仅仅从功能上划分,技术细节也是重要的。

====下面是程序运行时打印的log, 可以看出currentThread的classloader都是null(
Some implementations may use null to represent the bootstrap class loader.)。而不同plugin的classloader是不同的。
Current Thread's classLoader: null
BtafHibernateUtil's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4
Configuration's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4
cf's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1d520c4


Current Thread's classLoader: null
SessionFactory's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579
Configuration's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579
cf's classLoader: org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@1c6f579

2009年2月24日星期二

纠错对hibernate sessionFactory的使用

现状:
1 程序中使用username/password。硬编码。
2 待连接的数据库IP可能变化,并且不是从默认的hibernate.cfg.xml读取,而是从eclipse的preference本地存储读取。
3 获取hibernate sessionFactory的代码放在common包里。而数据库的IP配置是在btaf包里。

问题:

因为database IP可以动态改变,所以,HibernateUtil传统的静态初始化已经不能使用。同时,对preference中IP的依赖要传递到HibernateUtil中。
DBConnInfo在构造时保存从preference中获得的IP等信息,并提供方法返回DB URL。但是,当用户更改了IP后不能够构造新的sessionFactory.

解决:

1)在common包中创建HibernateUtil类。提高静态方法 public synchronized static SessionFactory getSessionFactory(String url);
这个方法根据url构建hibernate SessionFactory实例。如果传入的url不同于上个,则需要首先关闭以前创建的sessionFactory.
2) 所有获得sessionFactory的地方,都通过HibernateUtil.getSessionFactory(url)获得。不能把sessionFactory作为类变量保存。这样就保证了sessionFactory的单例。
3)在UI上更改DB IP后,要更新DBConnInfo实例。因为DBConnInfo是单例,所以获得url可以保证最新。

经验:
  • Hibernae的SessionFactory代表了对数据库的连接资源。
  • 如果你的应用中只需要用一个用户名密码对一个数据库连接,那么,一定要保证对SessionFactory实例是单例的。
  • 对sessionFactory 对象的引用生命期越短越好,最好不要出现在域变量中。
  • 如果你需要重新建立一个sessionFacotry,你需要保证对被废弃的sessionfactory object进行必要的清理工作。

**
DbConnInfo 这个类本来的目的就是保存数据库连接信息。但是,里面竟然出现了import ##.ui.preferences.PreferenceConstants;

2009年2月11日星期三

Fat Client中使用Hibernate C3P0

BTAF项目使用hibernate。作为胖客户端使用hibernate应该注意对连接池的配置。因为你不能控制客户端的在线时间,所以基本的原则是:尽少占用数据库连接、尽快释放连接。
所以
1)连接池最少连接数应该为零或者1;
2)最多缓存的连接数应该根据具体应用不同而不同;
3)从连接池中的撤销的周期应该短;
4) 检查过期连接的频率也应该频繁些。
下面是个示例:
<property name="c3p0.min_size">0</property>
<property name="c3p0.max_size">5</property>
<property name="c3p0.timeout">180</property>
<property name="c3p0.idleTestPeriod">60</property>
从hibernate的一些输出:
BasicResourcePool:1935 - Checking for expired resources - Thu Feb 12 11:04:32 CST 2009 [com.mchange.v2.resourcepool.BasicResourcePool@9ddde9]
BasicResourcePool:1447 - BEGIN check for expired resources. [com.mchange.v2.resourcepool.BasicResourcePool@9ddde9]
BasicResourcePool:1468 - FINISHED check for expired resources. [com.mchange.v2.resourcepool.BasicResourcePool@9ddde9]


关于如何在hibernate中配置C3P0:http://www.hibernate.org/214.html
关于c3p0更详细的介绍:http://www.mchange.com/projects/c3p0/index.html#configuration_properties

注意, hibernate文档中介绍说有些参数Must be set in hibernate.cfg.xml(or hibernate.properties),而有些Must be set in c3p0.properties。为什这样?因为必须在hibernate中设置的参数肯定经过了hibernate的封装,也许是因为这些参数太基本(重要)了。