hibernate存储过程
User.hbm.xml文件的内容如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"">
<hibernate-mapping package="com.amigo.proc.model">
<class name="User" table="tbl_user">
<id name="userid" column="userid">
<generator class="assigned"/>
</id>
<property name="name" column="name" type="string" />
<property name="blog" column="blog" type="string" />
</class>
<sql-query name="getUserList" callable="true">
<return alias="user" class="User">
<return-property name="userid" column="userid"/>
<return-property name="name" column="name"/>
<return-property name="blog" column="blog" />
</return>
{call getUserList()}
</sql-query>
</hibernate-mapping>
在该文件中需注意<sql-query…></sql-query>中的这段代码,调用的存储过程在其中定义,并定义了调用存储过程后将记录组装成User对象,同时对记录的字段与对象的属性进行相关映射。
public class ProcTest {
/** *//**
* @param args
*/
public static void main(String[] args) throws Exception {
ProcTest proc = new ProcTest();
Session session = HibernateSessionFactory.getSession();
proc.testProcQuery(session);
proc.testProcUpdate(session);
System.out.println("update successfully");
proc.testProcInsert(session);
System.out.println("insert successfully");
proc.testProcDelete(session);
System.out.println("delete successfully");
session.close();
}
/** *//**
* 测试实现查询的存储过程
* @throws Exception
*/
private void testProcQuery(Session session) throws Exception {
//查询用户列表
List list = session.getNamedQuery("getUserList").list();
for (int i = 0; i < list.size(); i++) {
User user = (User) list.get(i);
System.out.print("序号: " + (i+1));
System.out.print(", userid: " + user.getUserid());
System.out.print(", name: " + user.getName());
System.out.println(", blog: " + user.getBlog());
}
}
/** *//**
* 测试实现更新的存储过程
* @throws Exception
*/
private void testProcUpdate(Session session) throws Exception {
//更新用户信息
Transaction tx = session.beginTransaction();
Connection con = session.connection();
String procedure = "{call updateUser(?, ?, ?)}";
CallableStatement cstmt = con.prepareCall(procedure);
cstmt.setString(1, "陈xx");
cstmt.setString(2, "");
cstmt.setString(3, "sterning");
cstmt.executeUpdate();
tx.commit();
}
/** *//**
* 测试实现插入的存储过程
* @throws Exception
*/
private void testProcInsert(Session session) throws Exception {
//创建用户信息
session.beginTransaction();
PreparedStatement st = session.connection().prepareStatement("{call createUser(?, ?, ?)}");
st.setString(1, "amigo");
st.setString(2, "阿蜜果");
st.setString(3, "");
st.execute();
session.getTransaction().commit();
}
/** *//**
* 测试实现删除的存储过程
* @throws Exception
*/
private void testProcDelete(Session session) throws Exception {
//删除用户信息
session.beginTransaction();
PreparedStatement st = session.connection().prepareStatement("{call deleteUser(?)}");
st.setString(1, "amigo");
st.execute();
session.getTransaction().commit();
}
}
在本类中,调用查询类存储过程时,调用session.getNamedQuery("…")方法来获得User.hbm.xml中配置的查询存储过程。在其余的存储过程调用的测试中,首先通过hibernate的session获得connection,然后调用connection对象的相应方法来实现存储过程的调用。该类的执行结果如下:
Hibernate: {call getUserList()}
序号: 1, userid: ant, name: 蚂蚁, blog: [url]http://www.blogjava.net/qixiangnj[/url]
序号: 2, userid: beansoft, name: bean, blog: [url]http://www.blogjava.net/beansoft[/url]
序号: 3, userid: sterning, name: 似水流年, blog: [url]http://www.blogjava.net/sterning[/url]
序号: 4, userid: tom, name: tom, blog: [url]http://www.blogjava.net/tom[/url]
update successfully
insert successfully
delete successfully
五.总结
本例提出了在hibernate3中调用mysql的存储过程的实现方案,从本例可以看出,hibernate提供了在*.hbm.xml中配置调用存储过程,并通过向用户提供session.getNamedQuery(“…”)方法来调用配置的调用查询相关的存储过程的方法,另外,hibernate还提供了取得sql的connection的方法,从而能够通过connection中存储过程调用相关的方法来实现存储过程的调用。DamYankee 2007-12-13 18:44
[url]http://java.ccidnet.com/art/3737/20030124/472461_1.html[/url]
DamYankee 2007-12-15 15:48Hibernate 要点
1. 三W What, Why, When 什么是, 为什么, 什么时候
what:是一个 OR Mappming(O Object 对象 R relative 关系数据库 映射) 框架(framework) why: 把一个对象的人 分解成一张横的表的几列 Person => id, name, age 根据对象自动生成对应的关系数据库的 SQL, 可以简化 Java 数据库开发, 代替 JDBC 来实现持久化(Persistence). when: 1) 必须有明确的对象设计的时候才能用 Hibernate. 2) 数据库是用大量的存储过程实现的 不能用 Hibernate !!! 3) 如果多表查询, 也要慎重 Hibernate, 查询顺序不可预料(createSQLQuery(手写查询语句)) 2. 怎么用 1) 全局配置信息被 Configuration 类解析(配置解析器) a) 怎么连接数据库 hibernate.cfg.xml 获取数据库连接的参数(URL, username, password) 方言 (Dialect) 为了对不同的数据库生成相应的 SQL b) 有那些映射文件需要处理Configuration 类
Configuration类负责管理 Hibernate 的配置信息。Hibernate 运行时需要获取一些底层实现的基本信息,其中几个关键属性包括: 数据库URL 数据库用户 数据库用户密码 数据库JDBC驱动类 数据库dialect,用于对特定数据库提供支持,其中包含了针对特定数据库特性的实现,如Hibernate数据类型到特定数据库数据类型的映射等。 使用 Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准备。这些属性在hibernate配置文件(hibernate.cfg.xml )中加以设 定. 调用: Configuration config = new Configuration().configure(); 时,Hibernate会自动在当前的 CLASSPATH 中搜寻 hibernate.cfg.xml文件并将其读取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取 SessionFactory时需要涉及,当获取 SessionFactory 之后,由于配置信息已经由 Hibernate 维护并绑定在返回的SessionFactory之上,因此一般情况下无需再对其进行操作。示例的配置文件:
<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" ""><hibernate-configuration>
<session-factory>
<!-- 显示后台的 SQL, 便于调试 --><property name="show_sql">true</property> <property name="connection.username">classiccars</property> <property name="connection.url"> jdbc:derby://localhost:1527/myeclipse;create=true </property> <property name="dialect">org.hibernate.dialect.DerbyDialect</property> <property name="connection.driver_class">org.apache.derby.jdbc.ClientDriver</property> <property name="connection.password">myeclipse</property> <!-- 实体映射文件 --> <mapping resource="dao/User.hbm.xml" /></session-factory>
2) 实体类(POJO Plain and Old Java Ojbect) JavaBean 的要求 值对象, 只有 getter, setter, 没有业务方法 什么样的对象需要映射 public class User implements java.io.Serializable { private int id; private String username; getxxx setxxx }a) 要有主键字段.
b) 可序列化(缓存, 有时候在内存, 有时候放硬盘)3) 实体映射文件 实体名.hbm.xml
告诉 Hibernate 怎么来做对象映射. 向哪个表插入数据, 每个属性的数据类型, 以及对应数据表里的列名. 一个文件配置多个实体类也是可以的, 一般来说是一个实体一个配置文件.<?xml version="1.0" encoding="utf-8"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"""><hibernate-mapping> <class name="dao.User" table="users(数据库表格)" catalog="数据库名字"> <!-- 主键字段配置, hibernate为我们生成主键id, 必须定义--> <id name="id" type="java.lang.Integer"> <column name="id" /> <generator class="increment" /> <!-- increment 是先从数据库取最大ID然后加1, 再存入数据库 assigned 必须手工赋值给一个 ID auto, identify, sequence, native, uuid.hex, hilo 等等 --> </id> <!-- property 默认把类的变量映射为相同名字的表列,当然我们可以修改其映射方式--> <!-- 类型写法两种 Hibernate type: string, int; Java 类的全名: java.lang.Integer --> <property name="username" type="java.lang.String"> <!-- 指定对应数据库中的字段信息 --> <column name="username" length="200" not-null="true" /> </property> <property name="password" type="java.lang.String"> <column name="password" length="20" not-null="true" /> </property> <property name="age" type="java.lang.Integer"> <column name="age" /> </property> </class></hibernate-mapping>多对多几乎没人用, 一对一常用, 一对多比较常用.
3. SessionFactory 和 Session
SessionFactory ==> 等价于 DriverManager, 只需要一个.SessionFactory 负责创建 Session 实例。可以通过 Configuation 实例构建SessionFactory:SessionFactory sessionFactory = config.buildSessionFactory();
Configuration实例config会根据当前的配置信息,构造 SessionFactory实例并返回。SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后 config 的任何变更将不会影响到已经创建的 SessionFactory 实例(sessionFactory)。如果需要使用基于改动后的 config 实例的 SessionFactory,需要从 config 重新构建一个SessionFactory实例。 Session ==> 等价于 JDBC中的 ConnectionSession实例通过SessionFactory实例构建: Session session = sessionFactory.openSession();完整示例代码:
// 0. 加载配置和驱动等, 生成 Session 工厂(相当于连接池或者 DriverManager)
Configuration config = new Configuration().configure(); SessionFactory sessionFactory = config.buildSessionFactory(); // 1. 打开 session Session session = sessionFactory.openSession(); // 2. 打开事务(Transaction) org.hibernate.Transaction tran = session.beginTransaction(); // 3. 生成实体类 User bean = new User(); // 4. 给 bean 赋值 bean.setUsername("zhangsan"); // 5. 保存或者更新(并没有立即保存到数据) session.save(bean); // 6. 提交事务(真正的保存或者更新数据) tran.commit();// 7. 做查询, 首先创建查询对象
String queryString = "from User";// HSQL 操作的是实体, 不是数据库表格 Query query = getSession().createQuery(queryString); // 8. 读取查询结果 java.util.List<User> result = query.list();4. Transaction 事务
Hibernate 是 JDBC 的轻量级封装,本身并不具备事务管理能力。在事务管理层,Hibernate将其委托给底层的JDBC或者JTA,以实现事务管理和调度功能。 Hibernate的默认事务处理机制基于JDBC Transaction。我们也可以通过配置文定采用JTA作为事务管理实现:<property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JTATransactionFactory <!--net.sf.hibernate.transaction.JDBCTransactionFactory--></property> 将事务管理委托给 JDBC 进行处理无疑是最简单的实现方式,Hibernate 对于 JDBC事务的封装也极为简单。 我们来看下面这段代码: session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); …… tx.commit(); 从JDBC层面而言,上面的代码实际上对应着: Connection dbconn = getConnection(); dbconn.setAutoCommit(false); …… dbconn.commit(); 就是这么简单,Hibernate并没有做更多的事情(实际上也没法做更多的事情),只是将这样的JDBC代码进行了封装而已。5. 查询对象 Query(Hibernate 参考文档的第11章) 和 HSQL
普通查询如上所示 带参数的查询(相当于 PreparedStatement) q = session.createQuery("from User u where u.name= :name"); q.setString("name", "张三"); q = session.createQuery("from User u where u.name= ?"); q.setString(0, "张三"); Native SQL 方式(用于特定的数据库的查询) List cats=session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10","cat",Cat.class).list();Listcat s=session.createSQLQuery("SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex},"+"{cat}.MATE AS{cat.mate}, {cat}.SUBCLASSAS {cat.class},..."+"FROM CAT {cat} WHERE ROWNUM<10","cat",Cat.class).list(); 过滤重复记录: select distinct cat.name from Cat cat 6. 一对多, 多对一, 多对多中的懒惰加载 Lazy, Fetch新版本的Hibernate在处理Session的时候已经内置了延迟加载机制,只有在真正发生数据库操作的时候,才会从数据库连接池获取数据库连接. 这会带来问题. 例如一对多: <set name="addresses" table="t_address" lazy="false" inverse="false" cascade="all" > ... <one-to-many class="org.hibernate.sample.TAddress" /> </set> 示例代码: Query q = getSession().createQuery("from User");List userList = q.list(); TUser user =(TUser)userList.get(0); System.out.println("User name => "+user.getName()); Set hset = user.getAddresses(); session.close();//关闭Session TAddress addr = (TAddress)hset.toArray()[0]; System.out.println(addr.getAddress()); 运行时抛出异常: LazyInitializationException - Failed to lazily initialize a collection - no session or session was closed 如果我们稍做调整,将session.close放在代码末尾,则不会发生这样的问题。但是一般 DAO 执行结束后即关闭了 session.这意味着,只有我们实际加载user关联的address时,Hibernate才试图通过session从数据库中加载实际的数据集,而由于我们读取address之前已经关闭了session,所以报出session已关闭的错误。 解决办法:1) Hibernate.initialize方法可以通过强制加载关联对象实现这一功能:Hibernate.initialize(user.getAddresses());
session.close(); 2) 用 HQL 里面的 fetchfrom User fetch all properties3) lazy="false"
7. Hibernate 分页int currentPage = 0;// 当前页int pageSize = 10;// 显示记录数String queryString = "from User";Query queryObject = getSession().createQuery(queryString);// 设置从哪里读queryObject.setFirstResult((currentPage - 1) * pageSize);// 设置一共读几行queryObject.setMaxResults(pageSize);return queryObject.list();