前几天有时间看上学时的教材《ssh整合开发详解》,看到hibernate部分的时候,想着光看没意思,找个例子敲敲看咋样。结果一大堆问题,花了一天功夫才搞定所有的问题,运行出来书上的效果。想着写一下吧,以后自己忘了还可以看看,刚开始学hibernate的同学看看说说不定有帮助。
闲言少叙,先贴出来这个例子的整体结构及流程:
整体结构有了,现在从右边开工,建数据库acc密码acc 还有sid是orcl,这些信息等会儿要在hibernate.cfg.xml中设置,最后建一个名为member的表:
CREATE TABLE "ACC"."MEMBER"
(
"ID" NUMBER NOT NULL ENABLE,
"USERNAME" VARCHAR2(20 BYTE),
"PASSWORD" VARCHAR2(20 BYTE),
CONSTRAINT "MEMBER_PK" PRIMARY KEY ("ID") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "USERS" ENABLE
)
和mysql不同,oracle好像通过序列实现主键的自增,下面建立一个对应的序列:
CREATE SEQUENCE "ACC"."MEMBER_SEQ" MINVALUE 1 MAXVALUE 999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE ;
表有了,开始配置Hibernate.cfg.xml文件
<pre class="html" name="code"><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.username">acc</property>
<property name="connection.password">acc</property>
<property name="show_sql">true</property>
<property name="connection.url">
jdbc:oracle:thin:@localhost:1521:orcl
</property>
<property name="connection.dialect">
org.hibernate.dialect.Oracle10gDialect
</property>
<property name="connection.driver_class">
oracle.jdbc.driver.OracleDriver</property>
<mapping resource="member/Member.hbm.xml"/>
</session-factory>
</hibernate-configuration>
这里面信息量很大 ,除了username 和password两个属性分别属于数据库的用户名密码这事儿一清二楚之外,别的都不好理解或者不好找对应的值,至少对于我来说是这样的。所以下面一个个攻克:
<property name="show_sql">true</property>
这个是控制程序和数据库交互的时候sql是否显示的开关,设置为true就显示,false不显示。我这是在练习和测试,因此设置为true。
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
jdbc:表示采用jdbc方式连接数据库,
oracle:表示连接的是oracle数据库,
thin:表示连接时采用thin模式(oracle中有两中模式)
jdbc:oralce:thin:是一个jni方式的命名
@表示地址1521和orcl表示端口和数据库名
orcl这个位置说是要写数据库名,因为我对oracle数据库不熟悉,第一次写弄错了,写成了用户(user)的名字
结果报:
org.hibernate.exception.JDBCConnectionException: Error calling Driver#connectCaused by: java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection
感觉其实是指的sid的名字。因为确实不了解sid,就去查了一下orale,在FAQ中有这样一段说明:
The Oracle System ID (SID) is used to uniquely identify a particular database on a system. For this reason, one cannot have more than one database with the same SID on a computer system.
When using RAC, all instances belonging to the same database must have unique SID's.
sid是唯一的,这样的话有端口号等前面的信息加上唯一的sid就能指向特定的数据库了,就像orale为使用它的用户提供的访问接口一样。
<propertyname="connection.dialect"> org.hibernate.dialect.Oracle10gDialect</property>
连接用的方言,可以在hibernate文档中找到对应的配置:
Oracle 10g and later | org.hibernate.dialect.Oracle10gDialect |
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
不同的数据库jdbc连接的jar包不一样,我用的oracle10,jar包就在安装文件oracle\product\10.2.0\db_2\jdbc\lib文件夹下,这个还要加到项目lib包中。
<mappingresource="member/Member.hbm.xml"/>
要把需要持久话的类的对应hbm文件的路径写上。
至此hibernate.cfg.xml文件配好了,下面开始写session工厂类:
package hibernatesession;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateSessionFactory {
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
private static Configuration configuration = new Configuration();
private static org.hibernate.SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry = null;
static {
try {
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
System.out.println("-------------------静态代码块儿随项目启动执行了-------------------");
System.out.println("-------------------构建了一个sessionFactory:" + sessionFactory.toString() + "-------------------");
} catch (Exception e) {
e.printStackTrace();
}
}
public static Session getSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session == null || !session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession() : null;
threadLocal.set(session);
}
System.out.println("---------------取得了session:" + session.toString() + "--------------");
return session;
}
private static void rebuildSessionFactory() {
try {
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
System.out.println("------------------重新构建了factory:" + sessionFactory + "----------------");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
threadLocal.set(null);
if (session != null) {
session.close();
}
System.out.println("---------------关闭了session:" + session.toString() + "--------------");
}
}
在系统启动的时候,该类会自动到项目class路径下找hibernate.cfg.xml文件,读取其中的配置信息,并且建立连接。这里面需要注意的是:原来的configeration.buildSessionFactory()的方法初始化sessionFacroty已经过时了,我查了一下,找到要用上面写的方法。
工厂类建好之后,和数据库连接这一块儿就差不多了,下面开始项目结构和流程图的左边部分的工作:
先建立Member类:
package member;
public class Member {
private Integer id;
private String username;
private String password;
public Member() {
super();
}
public Member(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
这个很简单,就不多说了,下面介绍关键的映射xml配置即:Member.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="member.Member" table="member" catalog="acc">
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="native">
<param name="sequence">member_seq</param>
</generator>
</id>
<property name="username" type="string">
<column name="username" length="20"></column>
</property>
<property name="password" type="string">
<column name="password" length="20" ></column>
</property>
</class>
</hibernate-mapping>
这里面有两个需要特别注意的地方:
一个是<class name="member.Member" table="member" catalog="acc">这里面的catalog属性,是和hibernate.cfg.xml中的connection.username属性值是一致的,默认是可以不写的,但是如果写了并且写错了就会报异常:
org.hibernate.engine.jdbc.spi.SqlExceptionHelperlogExceptions
WARN: SQLError: 942, SQLState: 42000
org.hibernate.engine.jdbc.spi.SqlExceptionHelperlogExceptions
ERROR:ORA-00942:表或视图不存在
另一个就是主键映射配置<id name="id" type="java.lang.Integer">这里面的type属性和下面property标签的type属性也可以不写,但是写错了的话会报异常:
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.AbstractMethodError: oracle.jdbc.driver.T4CConnection.createClob()Ljava/sql/Clob;
org.hibernate.MappingException: Could not determine type for: java.long.Integer, at table: member, for columns: [org.hibernate.mapping.Column(id)]
这里面是把java.lang.Integer写成java.long.Integer了。
为了弄清楚type到底该怎么配置我查了一下,是这样说的:
hibernate中的类型就是8种基本类型的包装类首字母变为小写,有integer,long,short,float,double,charchter,byte,boolean,当然还包括其他类型如:yes_no,true_false
hibernate中的类型的作用是匹配数据库表中字段的类型和Java类中属性的类型不匹配的问题的一种解决方案,比如string完成从java.lang.String到VARCHAR的映射,所以在xxx.hbm.xml映射文件中我们写属性的类型时会写成如type="string"或者直接写成type="java.lang.String",后一种的效率更高,因为hibernate的类型也要转化为Java的类型才去匹配数据库中对应的字段类型。
当然现在可以直接在model类中直接通过注解的方式即可,不用那么麻烦了。
主键配置的时候还有一个关键的地方就是生成策略里面的序列的配置:<param name="sequence">member_seq</param>
连接orcale时,id下的值生成器generator要指定序列或者数据库中建立默认序列
否则报:
Hibernate: select hibernate_sequence.nextval from dual
2015-7-30 14:26:16 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 2289, SQLState: 42000
2015-7-30 14:26:16 org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: ORA-02289: 序列不存在
这是因为oracle建表时没有自动增长设置,在没有手工指定序列的时候,hibernate会默认数据库中创建了hibernate_sequence作为表新增主键的值。
上面的工作都完成之后就可以放心的建立Dao类操作Member实体了:
package member;
import hibernatesession.HibernateSessionFactory;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class MemberDao {
public Session getSession() {
return HibernateSessionFactory.getSession();
}
public void save(Member transientInstance) {
try {
Transaction tx = getSession().beginTransaction();
getSession().save(transientInstance);
tx.commit();
} catch (RuntimeException e) {
e.printStackTrace();
}
}
public List findByProperty(String property, Object value) {
List list = new ArrayList();
try {
String hsql = "from Member as model where model." + property + "=?";
Query query = getSession().createQuery(hsql);
query.setParameter(0, value);
list = query.list();
} catch (RuntimeException e) {
e.printStackTrace();
}
return list;
}
}
下面是测试类:
package member;
public class Test {
public static void main(String[] args) {
try {
MemberDao md = new MemberDao();
// 创建一个Student对象
Member stu = new Member();
// 通过Member的setter方法改变它的属性
// 注意Member_id不用我们设置
stu.setUsername("zhangsan");
stu.setPassword("18");
// 通过session的save()方法将Member对象保存到数据库中
md.save(stu);
} catch (Exception e) {
System.out.print("数据库中不存在该人");
e.printStackTrace();
}
}
}
最后感谢伟大的互联网及对我写这篇博客很有帮助的博客资料:
Struts2+Hibernate+Spring整合开发技术详解—第一个hibernate程序
http://wangyj0898.blog.51cto.com/1519857/380092 --hibernate demo
http://blog.csdn.net/u012110719/article/details/46405075 --hibernate成员变量类型
hibernate文档:
帮助很大,在hibernate的下载文件中:
hibernate-release-4.3.10.Final\hibernate-release-4.3.10.Final\documentation\devguide\en-US\html\index.html。
由于本人水平有限,难免有纰漏之处,请看到的高手们多多指点,希望上面的文字不会误导读者。