(2)配置及用法之Hibernate

(二)配置及用法之Hibernate
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。本文讲述Hibernate2的一些配置用法等,可能本文写得不是很详细和通俗易懂,希望和大家一起讨论和学习,进入正题。
Hibernate目前用到的包:
antlr-2.7.6.jar、commons-collections-3.1.jar、dom4j-1.6.1.jar、hibernate3.jar、hibernate-jpa-2.0-api-1.0.0.Final.jar、javassist-3.12.0.GA.jar、jta-1.1.jar、slf4j-api-1.6.1.jar、slf4j-nop-1.6.1.jar
(1)hibernate.cfg.xml的配置:

<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
   <property name="dialect">org.hibernate.dialect.MySQLDialect</property>//SQL方言
   <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
   <property name="connection.url">jdbc:mysql://localhost:3306/hibertest?useUnicode=true&amp;characterEncoding=UTF-8</property>
   <property name="connection.username">root</property>
   <property name="connection.password">123</property>
   <property name="hbm2ddl.auto">update</property>//还有create,validate,create-drop等属性的设置
   <property name="show_sql">true</property>//显示SQL语句到控制台
   <!--配置持久化类-->
   <mapping class="com.xxx.entity.Parent" />//使用Annotation时的设置
   <mapping resource="com/xxx/entity/Score.hbm.xml" />//使用Score.hbm.xml的设置		
</session-factory>
</hibernate-configuration>

Hibernate的基本应用:
配置Session:
	private static Configuration configuration = new Configuration();//Configuration实例(用于加载配置)
	private static SessionFactory sessionFactory;//session工厂
	private static ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();//当地线程,用户获取和设置session
	public static SessionFactory getSessionFactory() {
		sessionFactory = configuration.configure().buildSessionFactory();
 //configuration.configure()默认加载classpath下(也就是src)下的hibernate.cfg.xml,我们可以为其指定路径configuration.configure("url");
                return sessionFactory;
	}
   
	public static Session getSession() {
		Session session = (Session) threadLocal.get();

		if (session == null || !session.isOpen()) {//判断session是否为空或者是否打开,如果不为空还创建,那么每次使用getSession()都创建一个新的session,自然对数据库的存取操作出现问题。其实还有很多种办法,但是核心还是我们每次对数据库操作的session的唯一性。这样说并不代表不关闭Session,关闭Session是线程安全的行为。只是说的是每次的操作!
			if (sessionFactory == null) {
				sessionFactory = getSessionFactory();
			}
			session = sessionFactory.openSession();
		}
		threadLocal.set(session);//设置session
		return session;
	}
//注意:每次我们对数据库进行操作完后最好都要关闭Session。

事务封装例子:
	public static boolean save(Object object) {
		boolean flag = false;
		Transaction tx = null;
		try {
			tx = getSession().beginTransaction();//打开事务
			getSession().save(object);
			tx.commit();//提交事务
			flag = true;
		} catch (Exception e) {
			e.printStackTrace();
			tx.rollback();//回滚事务

		} finally {
			if (getSession() != null) {
				getSession().close();
			}
		}
		return flag;
	}

例子用所使用的持久化类:
//Student.class
public class Student {
	private Integer uid;
	private String studentName;
	private Set<Score> score= new HashSet<Score>();//一个学生有多个成绩,所以需要用Set集合。
        //private Score score;基于主键的双向一对一使用,不需要Set集合了。
//getter和setter方法略
}
//Score.class
public class Score {
	private Integer sid;
	private String scoreName;
	private Student student;//关联类Student使用的属性
//getter和setter方法略
}

(2)Score.hbm.xml的配置:(student.hbm.xml不列举,我只是大概描述配置,希望具体的用法还是让大家去实践,实践出真理!)
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="com.xxx.entity.Score" table="score" lazy="true">
	<id name="sid" column="sid">//主键设置
		<generator class="native" />//根据数据库选择生产策略,Mysql是自动增长
	</id>
    <property name="scoreName" column="scoreName" type="java.lang.String" />
	<many-to-one name="student" class="com.xxx.entity.Student" cascade="all" fetch="select" >//fetch="select",设置抓去策略select,Hibernate会发送一条select语句来抓去当前对象的关联实体和集合,实际应用中优化有限,不值得过多关注
		<column name="kid" ></column>
	</many-to-one>
  </class>
</hibernate-mapping>

(3)简要说明一下各xml里标签的应用:
  <!--普通的配置-->
  <id name="sid" column="sid" type="java.lang.Integer">//主键设置,name为此实体类的id,column为表的对应字段
     <generator class="native" />//根据数据库选择生产策略,Mysql是自动增长
     //<generator class="assigned"/>主键是由人工分配的  
  </id> 

  <property name="scoreName" column="scoreName" type="java.lang.String" />//name为实体类的属性,column同理,type为字段类型,整型为java.lang.Integer  

<!--多对一配置-->在Score.hbm.xml中
  <many-to-one name="student" class="com.xxx.entity.Student" cascade="all" fetch="select" >
	<column name="kid" ></column>
  </many-to-one>
//fetch参数指定了关联对象抓取的方式是select查询还是join查询,select方式时先查询返回要查询的主体对象(列表),再根据关联外键id,每一个对象发一个select查询,获取关联的对象,形成n+1次查询;而join方式,主体对象和关联对象用一句外键关联的sql同时查询出来,不会形成多次查询。(默认是select),fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合,此时lazy会失效。
//cascade有all(级联所有操作,等同于save-update和delete),save-update(级联保存更新等操作),delete(级联删除操作),none(不级联任何操作)
//<column name="kid" ></column>指定外键名

<!--基于主键单向一对一-->让Score当成外表,是我们的选择把?呵呵
//在Score.hbm.xml中:
  <id name="sid" column="sid" type="java.lang.Integer">
     <generator class="foreign">
        <param name="property">student</param>//设置为foreign,把Score的主键关联至Student的主键,让Score的主键按照Student的主键生成。不能让其自动增长。
      </generator>
   </id> 
    <one-to-one name="student" constrained="true"/>//name设置的是关联类(在Score类中)constrained="true"表明该主键由关联类生成
// one-to-one 有个property-ref属性,是对应你要指定到那个字段的引用,不设置时,默认为引用表的主键。
 

<!--基于主键的双向一对一-->
//根据单向一对一,只需要对student.hbm.xml补充设置Student关联Score就好了
<one-to-one name="score" cascade="all">

<!--基于外键的单向一对一-->//只在score.hbm.xml中设置
<many-to-one cascade="all" name="student" column="sid" not-null="true" unique="true" class="com.xxx.entity.Student"/>
//unique="true" 表示多的一端也唯一

<!--双向一对多关联-->
//在Student.hbm.xml中:
<set name="scores" cascade="save-update" inverse="true">
 <key><column name="tid" not-null="true"></key>//tid为外键
 <one-to-many class="com.xxx.entity.Score"/>
</set>
//inverse="true"是让Student为主控方,由受控方Score维护,让一端维护的话,那么每次在删除等操作的时候,都会让一端去更新其关联的每一个多端,如果让多端维护,那么就不会有更新操作,数据库效率更高些。
//在Score.hbm.xml中:
<many-to-one fetch="select" cascade="save-update" name="student" class="com.xxx.entity.Student">
 <column name="tid" not-null="true"/>
</many-to-one>

(4)Annotation配置Hibernate:
//这里就以实际的配置大概讲述一下,也许不是很完善,但是具体的用法还是需要我们去实践,再说一次,实践出真理!当用Annotation配置的时候,我们不需要xxx.hbm.xml这个文件了,所以hibernate.cfg.xml的映射也需要相关的改变,请参照hibernate。cgf.xml的配置
//Student.class中的配置:
@Entity//声明为持久化类
public class Student {
	private Integer uid;
	private String studentName;
	private Set<Score> set = new HashSet<Score>();
	public void setSet(Set<Score> set) {
		this.set = set;
	}
	@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="student")//一对多的配置,注意要使用mappedBy="Score类里的Student变量名",如果不使用mappedBy,那么会生成一个中间表来存储双方的关联关系,这样岂不是复杂了?mappedBy只在一对多、多对多、一对一里才有。多对一没有。cascade属性也就是级联关系,PERSIST(级联保存) REMOVE(级联删除) REFRESH(级联刷新) MERGE(级联更新) ALL(级联所有)
	public Set<Score> getSet() {
		return set;
	}

	@Id//主键
	@GeneratedValue(strategy = GenerationType.AUTO)//主键生成策略(自增)
	public Integer getUid() {
		return uid;
	}
	public void setUid(Integer uid) {
		this.uid = uid;
	}
	public String getStudentName() {
		return studentName;
	}
	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}
}

//Score.class中的配置:
@Entity
public class Score {
	private Integer sid;
	private String scoreName;
	private Student student;
	@Id//主键
	@GeneratedValue//主键生成策略(默认也是自增)
	public Integer getSid() {
		return sid;
	}
	public void setSid(Integer sid) {
		this.sid = sid;
	}
	public String getScoreName() {
		return scoreName;
	}
	public void setScoreName(String scoreName) {
		this.scoreName = scoreName;
	}
	public void setStudent(Student student) {
		this.student = student;
	}
	@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
	@JoinColumn(name="tid")//设置多的一方的表中的外键
	public Student getStudent() {
		return student;
	}

对于基于主键的双向一对一的Annotation配置,弄不出来,希望懂的可以留言。
好了,暂且我先介绍到这里,以后会慢慢完善Hibernate的配置,注意设置延迟加载的利弊,当Lazy="true",如果关闭了Session再进行获取就会出错。当然,可以通过一些配置来解决这个问题,去网上搜索一下把。网上的资源我们得充分利用!
总结:(1)主要开发过程hibernate.cfg.xml+xxx.hbm.xml+实体类
      (2)如果用Annotation配置实体类,那么xxx.hbm.xml可省略。开发模型为hibernate.cfg.xml+实体类
(3)当hibernate.hbm2ddl.auto=update不起作用时候在spring的session的sessionfactory加上: <property name="schemaUpdate">  
   <value>true</value>  
</property>