我在实际的项目应用中,有时会设计出这样的一种数据表,每个时间段产生一个新表,例如是按年或月或日。相同类型的表中,所有的字段结构都是一样的。而
hibernate 提供的类与表的映射,是只能映射到一个具体表的,在程序的运行过程中,很难去动态修改一个 hbm
对应的表名。我在网上也有看到一实现,但是很复杂,并且不符合我的要求。
因此我就想到直接用 jdbc 去操作数据库,这样的做法是绕过 hibernate 了。方法是从 hibernate 的
session 中,直接取得数据库 connection ,然后就直接 jdbc 了。
后来在升级了 proxool 到 9.0RC3 后,发现居然出现了数据库连接无法释放的问题。为了解决这个问题,我查阅了
hibernate doc。我发现原来用 SQLQuery 可以更好的解决,并且可以重新用于 hibernate hbm
机制。以下举例说明。
例如我有一个 pojo 是
ReadInfo,用来记录阅读信息的。由于数据量宠大,所以我的思路是按月划分,每个月一张表。所以只是表名不同,而字段是完全相同的。
ReadInfo.java 是这样的,其中 userId, year, month, day 是联合主键:
|
private
Integer
userId;
private
Integer
year;
private
Integer
month;
private
Integer
day;
private
Integer
point
;
|
那么相应的 ReadInfo.hbm.xml 的片段是
|
<
class
name=
"ReadInfo"
table=
"tblReadInfo
"
mutable
=
"false"
>
<
composite-id>
<
key-property
name=
"userId"
column=
"userId"
type=
"integer"
/
>
<
key-property
name=
"year"
column=
"year"
type=
"integer"
/
>
<
key-property
name=
"month"
column=
"month"
type=
"integer"
/
>
<
key-property
name=
"day"
column=
"day"
type=
"integer"
/
>
<
/
composite-id>
<
property
name=
"point"
column=
"point"
type=
"integer"
/
>
<
/
class>
|
上面的xml,注意 2 个细节
1. pojo 所映射的 table tblReadInfo 实际上是不存在的。实际的表是 tblRead200710
之类的;
2. mutable 要设置为 false,即是说,关闭 hibernate 对这个 pojo
的任何持久化操作,以避免 hibernate 把数据写到 tblReadInfo
中(这个表是不存在的嘛)。因此,所有的持久化操作,都是需要自己通过 SQLQuery 来处理。
现在可以看一下 ado 中的操作了,先看一个 select 操作
|
public
ReadInfo
selectReadInfo(
Integer
userId,
Integer
year,
Integer
month,
Integer
day)
throws
HibernateException
{
ReadInfo
readInfo =
null
;
Session
session =
getSession
(
)
;
Transaction
tx =
session.
beginTransaction(
)
;
try
{
String
sql
=
"select
*
from tblRead"
+
Misc.
formatMoon(
year,
month)
+
" where userId=? and
day=?"
;
SQLQuery
query
=
session.
createSQLQuery(
sql
)
;
query
.
addEntity(
ReadInfo.
class
)
;
query
.
setLong
(
0,
userId)
;
query
.
setInteger(
1,
day)
;
readInfo
=
(
ReadInfo)
query
.
uniqueResult(
)
;
tx.
commit
(
)
;
}
catch
(
HibernateException
e)
{
log
.
error
(
"catch
exception:"
,
e)
;
if
(
tx !
=
null
)
{
tx.
rollback
(
)
;
}
throw
e;
}
return
readInfo;
}
|
上面的代码,关键是以下几点:
1. 通过函数参数的 year, month 来确定要操作的表名,我自己写了一个 Misc.formatMoon(year,
month) 来生成 "yyyyMM" 格式的字串;
2. 使用了 SQLQuery ,再通过 query.addEntity(ReadInfo.class); 建立与
ReadInfo 的映射关系;
3. query.setXxx() 与 PreparedStatement 的类似,不过索引是从
0
开始;
4. 其它的就跟一般的 Query 操作类似的了。
再看一个 insert 操作
|
public
void
insertReadInfo(
ReadInfo readInfo)
throws
HibernateException
{
Session
session =
getSession
(
)
;
Transaction
tx =
session.
beginTransaction(
)
;
try
{
String
sql
=
"insert
into
tblRead"
+
Misc.
formatMoon(
readInfo.
getYear
(
)
,
readInfo.
getMonth
(
)
)
+
" (userId, year, month, day,
point)
values (?, ?, ?, ?, ?)"
;
SQLQuery
query
=
session.
createSQLQuery(
sql
)
;
query
.
setLong
(
0,
readInfo.
getUserId(
)
)
;
query
.
setInteger(
1,
readInfo.
getYear
(
)
)
;
query
.
setInteger(
2,
readInfo.
getMonth
(
)
)
;
query
.
setInteger(
3,
readInfo.
getDay
(
)
)
;
query
.
setInteger(
4,
readInfo.
getPoint
(
)
)
;
query
.
executeUpdate
(
)
;
tx.
commit
(
)
;
}
catch
(
HibernateException
e)
{
log
.
error
(
"catch
exception:"
,
e)
;
if
(
tx !
=
null
)
{
tx.
rollback
(
)
;
}
throw
e;
}
}
|
同理,update, delete 等操作也是这样实现的。
hmm.. 这种处理方式的麻烦的地方是需要手工写 sql ,因此要尽量写通用的标准
sql,不然在数据库兼容方面会有问题。当然,有时是会出现无法兼容的情况,那么可以考虑把 sql
写到配置文件中,根据不同的数据库,装载相应的配置文件咯。