JavaEE学习笔记——2、Hibernate基础

来源:互联网 发布:静态网页制作软件 编辑:程序博客网 时间:2024/06/14 23:38
==============================ORM的基本知识==============================

目前主流数据库是关系型数据库(RDB),而Java则是面向对象的编程语言(OOPL),要结合两者相当麻烦。

ORM是为了解决这一问题而产生的概念。


OOPL的优势:

面向对象的建模、操作

多态、继承

摒弃难以理解的过程

建安易用,易理解


RDB的优势:

大量数据查找、排序

集合数据连接操作、映射

数据库访问的并发、事务

数据库的约束、隔离


ORM,Object/Relation Mapping可理解为一种规范,它概述了这类框架的特征:完成OOPL到RDB的映射。

当ORM框架完成映射后,即可以利用OOPL的简单易用,又能使用RDB的技术优势。


其实JavaEE规范里的JPA规范就是一种ORM规范,只是JPA并不提供任何ORM实现,只提供了系列的编程接口。


==============================ORM和Hibernate的关系==============================


Hibernate是一个ORM框架,管理Java类到数据库表的映射,还提供查询和获取数据的方法,大幅度缩短使用JDBC处理数据持久化的时间。


==============================Hibernate的基本映射思想==============================


所有ORM工具大致上都遵循相同的映射思路:数据表映射类、数据表的行映射对象(即实例)、数据表的列(字段)映射对象的属性。


==============================Hibernate入门知识==============================

Hibernate被称为低侵入式设计,因为它采用POJO作为PO,不要求持久化类继承任何父类,或者实现任何接口。

Hibernate底层依然是基于JDBC的,因此应用程序使用它少不了JDBC驱动。

所有ORM框架中有一个非常重要的媒介:PO,通过该对象可对数据执行CRUD操作,以OO的方式操作DB。

数据源是一种提高数据库连接性能的常规手段,hibernate推荐采用c3p0数据源管理数据库连接。

在官网上下载Hibernate的压缩包后,里面的hibernate.jar和lib下的required、jpa子目录下所有jar包是必须的。

一个简单的示例:

//domain包下的POJOpublic class News {private Integer id;private String title;private String content;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}}


<?xml version="1.0" encoding="UTF-8"?><!-- 指定Hibernate3映射文件的DTD信息 --><!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><!-- hibernate mapping是映射文件的根元素 --><hibernate-mapping package="basicusage.chapter1"><!-- 每个class元素对应一个持久化对象 --><class name="News" table="t_newss"><!-- id元素定义持久化类的标识属性 --><id name="id"><!-- 指定主键生成策略 --><generator class="identity" /></id><!-- property元素定义常规属性 --><property name="title" /><property name="content" /></class></hibernate-mapping>

PO = POJO + hbm.xml

<?xml version="1.0" encoding="UTF-8"?><!-- 指定Hibernate配置文件的DTD信息 --><!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><!-- hibernate-configuration是连接配置文件的根元素 --><hibernate-configuration><session-factory><!-- 指定连接数据库所用的驱动 --><property name="contention.driver_class">com.mysql.jdbc.Driver</property><!-- 指定连接数据库的url,hibernate连接的数据库名 --><property name="connection.url">jdbc:mysql://192.168.56.111:3306/hibernateexercise</property><!-- 指定连接数据库的用户名 --><property name="connection.username">root</property><!-- 指定连接数据库的密码 --><property name="connection.password">123321</property><!-- 指定连接池的最大连接数 --><property name="hibernate.c3p0.max_size">20</property><!-- 指定连接池的最小连接数 --><property name="hibernate.c3p0.max_size">1</property><!-- 指定连接池的超时时间 --><property name="hibernate.c3p0.timeout">5000</property><!-- 指定连接池的最大缓存多少个Statement对象 --><property name="hibernate.c3p0.max_statements">100</property><property name="hibernate.c3p0.idle_test_period">3000</property><property name="hibernate.c3p0.acquire_increment">2</property><property name="hibernate.c3p0.validate">true</property><!-- 指定数据库方言 --><property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property><!-- 根据需要自动创建数据库表 --><property name="hibernate.hbm2ddl.auto">update</property><!-- 显示SQL --><property name="hibernate.show_sql">true</property><!-- 格式化良好地显示SQL --><property name="hibernate.format_sql">true</property><!-- 在SQL中添加有助于调试的注释 --><property name="hibernate.use_sql_comments">false</property><!-- 罗列所有的映射文件 --><mapping resource="basicusage/chapter1/News.hbm.xml" /></session-factory></hibernate-configuration>

持久化操作代码:

public class NewsManager{public static void main(String[] args) throws Exception{Configuration conf = new Configuration()// 下面的方法默认加载hibernate.cfg.xml.configure();SessionFactory sf = conf.buildSessionFactory();Session sess = sf.openSession();// 开始事务Transaction tx = sess.beginTransaction();News n = new News();n.setTitle("create title");n.setContent("create content");sess.save(n);// 提交事务tx.commit();sess.close();sf.close();}}

hibernate持久化通常操组步骤:
1、开发持久化类,由POJO加映射文件组成。
2、获取Configuration。
3、获取SessionFactory。
4、获取Session,打开事务。
5、用面向对象的方式操作数据库。
6、关闭事务,关闭Session。

对Hibernate的数据访问进行优化时,需要了解Hibernate底层SQL操作,这是因为数据访问对绝大部分应用来说,是一个巨大、耗时的操作。


==============================使用Eclipse开发Hibernate应用==============================


Eclipse.hibernate工具http://www.jboss.org/tools/download/stable

该工具可以通过数据库现存表的结构反向生成对应的实体类。


==============================Hibernate的体系和核心API==============================


现在我们知道了一个概念Hibernate Session,只有处于Session管理下的POJO才具有持久化操作能力。当应用程序对于处于Session管理下的POJO实例执行操作时,Hibernate将这种面向对象的操作转换成了持久化操作能力。

通过上图能够发现HIbernate需要一个hibernate.properties文件,该文件用于配置Hibernate和数据库连接的信息。还需要一个XML文件,该映射文件确定了持久化类和数据表、数据列之间的想对应关系。

除了使用hibernate.properties文件,还可以采用另一种形式的配置文件: *.cfg.xml文件。在实际应用中,采用XML配置文件的方式更加广泛,两种配置文件的实质是一样的。

Hibernate的持久化解决方案将用户从赤裸裸的JDBC访问中释放出来,用户无需关注底层的JDBC操作,而是以面向对象的方式进行持久层操作。底层数据连接的获得、数据访问的实现、事务控制都无需用户关心。这是一种“全面解决”的体系结构方案,将应用层从底层的JDBC/JTA API中抽象出来。通过配置文件来管理底层的JDBC连接,让Hibernate解决持久化访问的实现。


(1)SessionFactory这是Hibernate的关键对象,它是单个数据库映射关系经过编译后的内存镜像,它也是线程安全的它是生成Session的工厂,本身要应用到ConnectionProvider,该对象可以在进程和集群的级别上,为那些事务之间可以重用的数据提供可选的二级缓存。

(2)Session:它是应用程序和持久存储层之间交互操作的一个单线程对象。它也是Hibernate持久化操作的关键对象,所有的持久化对象必须在Session的管理下才能够进行持久化操作。此对象的生存周期很短,其隐藏了JDBC连接,也是Transaction 的工厂。Session对象有一个一级缓存,现实执行Flush之前,所有的持久化操作的数据都在缓存中Session对象处。

(3)持久化对象:系统创建的POJO实例一旦与特定Session关联,并对应数据表的指定记录,那该对象就处于持久化状态,这一系列的对象都被称为持久化对象。程序中对持久化对象的修改,都将自动转换为持久层的修改。持久化对象完全可以是普通的Java Beans/POJO,唯一的特殊性是它们正与Session关联着。

(4)瞬态对象和脱管对象:系统进行new关键字进行创建的Java 实例,没有Session 相关联,此时处于瞬态。瞬态实例可能是在被应用程序实例化后,尚未进行持久化的对象。如果一个曾今持久化过的实例,但因为Session的关闭而转换为脱管状态。

(5)事务(Transaction):代表一次原子操作,它具有数据库事务的概念。但它通过抽象,将应用程序从底层的具体的JDBC、JTA和CORBA事务中隔离开。在某些情况下,一个Session 之内可能包含多个Transaction对象。虽然事务操作是可选的,但是所有的持久化操作都应该在事务管理下进行,即使是只读操作。

(6)连接提供者(ConnectionProvider):它是生成JDBC的连接的工厂,同时具备连接池的作用。他通过抽象将底层的DataSource和DriverManager隔离开。这个对象无需应用程序直接访问,仅在应用程序需要扩展时使用。

(7)事务工厂(TransactionFactory):他是生成Transaction对象实例的工厂。该对象也无需应用程序的直接访问。


==============================Hibernate的配置文件==============================


除了通过xml配置外,还可以创建Configuration对象,通过属性设置的方式配置,不过实际开发中并不使用。

public class NewsManager{public static void main(String[] args) throws Exception{//实例化Configuration,不加载任何配置文件Configuration conf = new Configuration()//通过addClass方法添加持久化类.addClass(domain.News.class)//通过setProperty设置Hibernate的连接属性。.setProperty("hibernate.connection.driver_class" , "com.mysql.jdbc.Driver").setProperty("hibernate.connection.url" , "jdbc:mysql://localhost/hibernate").setProperty("hibernate.connection.username" , "root").setProperty("hibernate.connection.password" , "32147").setProperty("hibernate.c3p0.max_size" , "20").setProperty("hibernate.c3p0.min_size" , "1").setProperty("hibernate.c3p0.timeout" , "5000").setProperty("hibernate.c3p0.max_statements" , "100").setProperty("hibernate.c3p0.idle_test_period" , "3000").setProperty("hibernate.c3p0.acquire_increment" , "2").setProperty("hibernate.c3p0.validate" , "true").setProperty("hibernate.dialect" , "org.hibernate.dialect.MySQLInnoDBDialect").setProperty("hibernate.hbm2ddl.auto" , "create");//以Configuration创建SessionFactorySessionFactory sf = conf.buildSessionFactory();//实例化SessionSession sess = sf.openSession();//开始事务Transaction tx = sess.beginTransaction();//创建消息实例News n = new News();//设置消息标题和消息内容n.setTitle("it is title");n.setContent("it is content");//保存消息sess.save(n);//提交事务tx.commit();//关闭Sessionsess.close();}}

Hibernate\hibernate-distribution-3.6.10.Final\project\etc目录下有配置文件模板

hibernate提供了连接池配置hibernate.connection.pool_size,但自带的连接池仅有测试价值,实际项目中使用c3p0或proxool。


==============================持久化类的基本要求==============================


基本规则:

1、一个无参构造器,这是为了让Hibernate能使用Constructor.newInstance()来创建持久化类的实例。

2、提供一个标识属性,通常是主键字段,或者联合主键。

3、为持久化类的每个属性提供setter和getter方法,使之具有JavaBean风格。

4、使用非final的类,如果PO没有实现任何借口的话,Hibernate使用CGLIB生成代理,该代理对象是PO子类的实例。如果使用了final类,则无法生成CGLIB代理,将无法进行性能优化。

5、重写equals()和hashCode()方法。


==============================持久化对象的状态==============================


Transient(瞬态):对象由new创建,尚未与Session关联。

Persistent(持久化):持久化实例在DB中有对应的记录,拥有一个持久化标识(identifier)。

Detached(脱管):某个实例曾处于持久化状态,但随后与之关联的Session被关闭。

改变PO状态的方法:

save()和persist(),Hibernate之所以提供与save功能几乎类似的persist方法,一方面是为了照顾JPA的用法习惯,另一方面,它们之间有一个区别:save会返回PO的标识属性值,persist则没有返回值,因为save会立即将PO对应的数据插入DB,而persist则保证当它在一个事务外部被调用时,并不立即转换成insert语句,这个功能很有用,尤其当我们封装一个长会话流程的时候。

load()和get(),这两方法主要区别在于是否延迟加载,load具有延迟加载功能,它不会立即访问DB,当试图加载的记录不存在时,load可能返回一个未初始化的代理对象;而get总是立即访问DB,当试图加载的记录不存在时,返回null。

update()和updateOrSave(),前者保存应用对PO所做的修改,若不清楚该PO是否被持久化过,则使用后者进行自动判断。

merge()也可将应用对脱管对象所做的修改保存到DB,区别于update的是,merge不会持久化给定对象,举例来说:update(a)后,a对象将变为持久化状态,但merge(a)后,a对象不会变为持久化状态,a对象依然不被关联到session。

当使用merge保存应用对脱管对象所做的修改时,如果Session中存在相同持久化标识的PO,merge方法里提供对象的状态将覆盖原有PO的状态,如果Session中没有相应的PO,则尝试从DB中加载,或创建型额PO,最后返回该PO。

当使用load或get方法加载PO时,还可指定一个“锁模式”(LockMode),通过lock()方法。

Notice:Hibernate本身不提供直接执行update或delete语句的API,Hibernate提供的是一种OO的状态管理。


==============================Hibernate的基本映射==============================


映射文件以.hbm.xml结尾,每个Hibernate映射文件的基本结构都是相同的。


==============================List、Set、Map等级和属性映射==============================


List:

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的学校private List<String> schools = new ArrayList<String>();//id属性的setter和getter方法public void setId(Integer id){this.id = id;}public Integer getId(){return this.id;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//age属性的setter和getter方法public void setAge(int age){this.age = age;}public int getAge(){return this.age;}//schools属性的setter和getter方法public void setSchools(List<String> schools){this.schools = schools;}public List<String> getSchools(){return this.schools;}}

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射List集合属性 --><list name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射集合属性数据表的集合索引列 --><list-index column="list_order"/><!-- 映射保存集合元素的数据列 --><element type="string" column="school_name"/></list></class></hibernate-mapping>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}//创建并保存Person对象private void createAndStorePerson(){//打开线程安全的session对象Session session = HibernateUtil.currentSession();//打开事务Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建List集合List<String> schools = new ArrayList<String>();schools.add("小学");schools.add("中学");//设置List集合属性yeeku.setSchools(schools);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}}



Array:

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//数组属性,保留该对象关联的学校private String[] schools;//id属性的setter和getter方法public void setId(Integer id){this.id = id;}public Integer getId(){return this.id;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//age属性的setter和getter方法public void setAge(int age){this.age = age;}public int getAge(){return this.age;}//schools属性的setter和getter方法public void setSchools(String[] schools){this.schools = schools;}public String[] getSchools(){return this.schools;}}

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射数组属性 --><array name="schools" table="school"><!-- 映射数组属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射数组属性数据表的数组索引列 --><list-index column="list_order"/><!-- 映射保存数组元素的数据列 --><element type="string" column="school_name"/></array></class></hibernate-mapping>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}//创建并保存Person对象private void createAndStorePerson(){//打开线程安全的session对象Session session = HibernateUtil.currentSession();//打开事务Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建数组对象String[] schools = new String[]{"小学" , "中学"};//设置数组属性yeeku.setSchools(schools);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}}



Set:

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的学校private Set<String> schools = new HashSet<String>();//id属性的setter和getter方法public void setId(Integer id){this.id = id;}public Integer getId(){return this.id;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//age属性的setter和getter方法public void setAge(int age){this.age = age;}public int getAge(){return this.age;}//schools属性的setter和getter方法public void setSchools(Set<String> schools){this.schools = schools;}public Set<String> getSchools(){return this.schools;}}

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射Set集合属性 --><set name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列,增加非空约束 --><element type="string" column="school_name"not-null="true"/></set></class></hibernate-mapping>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}private void createAndStorePerson(){Session session = HibernateUtil.currentSession();Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建Set集合Set<String> s = new HashSet<String>();s.add("小学");s.add("中学");//设置Set集合属性yeeku.setSchools(s);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}}



bag:

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的学校private Collection<String> schools =new ArrayList<String>();//id属性的setter和getter方法public void setId(Integer id){this.id = id;}public Integer getId(){return this.id;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//age属性的setter和getter方法public void setAge(int age){this.age = age;}public int getAge(){return this.age;}//schools属性的setter和getter方法public void setSchools(Collection<String> schools){this.schools = schools;}public Collection<String> getSchools(){return this.schools;}}

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 使用bag元素映射集合属性 --><bag name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列 --><element type="string" column="school_name"not-null="true"/></bag></class></hibernate-mapping>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}//创建并保存Person对象private void createAndStorePerson(){//打开线程安全的session对象Session session = HibernateUtil.currentSession();//打开事务Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建List集合Collection<String> schools =new ArrayList<String>();schools.add("小学");schools.add("中学");//设置Collection集合属性yeeku.setSchools(schools);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}   }



Map:

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的考试成绩private Map<String ,Float> scores= new HashMap<String ,Float>();//id属性的setter和getter方法public void setId(Integer id){this.id = id;}public Integer getId(){return this.id;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//age属性的setter和getter方法public void setAge(int age){this.age = age;}public int getAge(){return this.age;}//scores属性的setter和getter方法public void setScores(Map<String ,Float> scores){this.scores = scores;}public Map<String ,Float> getScores(){return this.scores;}}

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射Map集合属性 --><map name="scores" table="score"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射集合属性数据表的Map key列 --><map-key column="subject" type="string"/><!-- 映射保存集合元素的数据列 --><element column="grade" type="float"/></map></class></hibernate-mapping>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}private void createAndStorePerson(){//打开线程安全的session对象Session session = HibernateUtil.currentSession();//打开事务Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建Map集合Map<String ,Float> m = new HashMap<String ,Float>();m.put("语文" , 67f);m.put("英文" , 45f);//设置Map集合属性yeeku.setScores(m);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}}


性能分析

对于集合属性,通常推荐使用延迟加载。

集合可分成如下两类:有序(根据key或index访问)、无序(只能遍历元素)。

有序集合都拥有一个由外键列和集合元素索引列组成的联合主键,因此,集合属性的更新或删除是非常高效的——主键已被有效索引。

无序集合的主键由<key.../>和其他元素字段构成,或者根本没有主键。DB很难对复杂主键进行索引,即使可以建立索引,性能也非常差。

在这种情况下,<bag.../>映射是最差的,因为它允许有重复的元素值,也没有索引字段,如果试图修改这种集合属性,Hibernate会先删除全部,再重新创建。

对于多对多关联、值数据的集合而言,有序集合类比Set多一个好处:因为Set集合内部结构的原因,所以如果“改变”Set集合的某个元素,Hibernate不会立即update该元素对应的数据行,因此,只有insert或delete时“改变”才有效。

在Hibernate中,Set应该是最通用的集合类型,这是因为“Set集合”的语义最贴近关系模型的联系关系,因此Hibernate的关联映射都是采用<set.../>元素(或<bag../>元素)映射的。而且,在设计良好的Hibernate领域中,1-N关联的1的一端通常带有inverse="true",对于这种关联映射,1的一端不再控制关联关系,所有更新操作将会在N的一端进行处理,对于这种情况,无须考虑其集合的更新性能。

一旦指定inverse="true"属性,使用<list.../>和<bag.../>映射属性将有较好的性能,因为Collection.add()和Collection.addAll()方法总是返回true。而Set集合需要保证元素不能重复,这就会进行插入前比较。


==============================数据库对象映射==============================


==============================组合属性映射==============================


==============================集合元素为复合类型的映射==============================


==============================复合主键映射==============================


==============================使用JPA Annotation管理映射信息==============================


早期Hibernate使用XML映射文件管理实体类与表之间的映射关系,而JPA规范则推荐使用更简单、易用的Annotation来管理实体类与数据表之间的映射关系,这样就避免了一个实体需要同时维护两份文件(类与xml配置)。

@Entity@Table(name="person_table")public class Person{/* 指定使用复合主键类是Name */@EmbeddedId@AttributeOverrides({@AttributeOverride(name="first", column=@Column(name="person_first")),@AttributeOverride(name="last", column=@Column(name="person_last" , length=20))})private Name name;//普通属性@Column(name="person_email")private String email;@Embedded@AttributeOverrides({@AttributeOverride(name="name" , column=@Column(name="cat_name" , length=35)),@AttributeOverride(name="color" , column=@Column(name="cat_color"))})//组件属性,代表此人拥有的宠物private Cat pet;//name属性的setter和getter方法public void setName(Name name){this.name = name;}public Name getName(){return this.name;}//email属性的setter和getter方法public void setEmail(String email){this.email = email;}public String getEmail(){return this.email;}//pet属性的setter和getter方法public void setPet(Cat pet){this.pet = pet;}public Cat getPet(){return this.pet;}}

@Entity用于标注该类是一个PO,@EmbeddedId用于标注符合类型的标识属性,而@Embedded用于标注一个组件属性,也就是说Person的name属性就是一个复合类型的标识属性;pet属性是一个组件属性。

//修饰组件属性类@Embeddablepublic class Nameimplements java.io.Serializable{private String first;private String last;//无参数的构造器public Name(){}//初始化全部属性的构造器public Name(String first , String last){this.first = first;this.last = last;}//first属性的setter和getter方法public void setFirst(String first){this.first = first;}public String getFirst(){return this.first;}//last属性的setter和getter方法public void setLast(String last){this.last = last;}public String getLast(){return this.last;}//提供重写的equals方法public boolean equals(Object obj){if (this == obj){return true;}if (obj.getClass() == Name.class){Name target = (Name)obj;if (target.getFirst().equals(first)&& target.getLast().equals(last)){return true;}}return false;}//提供重写的hashCode方法public int hashCode(){return first.hashCode() + last.hashCode() * 17;}}

上面的Name类需要作为标识属性的类型,因此一样需要实现Serializable接口,并重写hashCode和equals方法。

至于Person类所包含的组件属性pet,它所属的Cat类也只要简单地使用@Embeddable修饰即可。

//修饰组件属性类@Embeddablepublic class Cat{private String name;private String color;//无参数的构造器public Cat(){}//初始化全部属性的构造器public Cat(String name , String color){this.name = name;this.color = color;}//name属性的setter和getter方法public void setName(String name){this.name = name;}public String getName(){return this.name;}//color属性的setter和getter方法public void setColor(String color){this.color = color;}public String getColor(){return this.color;}}

一旦在实体类中通过上面的Annotation进行标注之后,Hibernate已经能够理解实体类与表之间的映射关系了,也就不再需要.hbm.xml的映射文件了。

<!-- 原有的Person.hbm.xml需作更改 -->
<mapping class="org.crazyit.app.domain.Person"/>

public class PersonManager{public static void main(String[] args){PersonManager mgr = new PersonManager();mgr.createAndStorePerson();HibernateUtil.sessionFactory.close();}private void createAndStorePerson(){Session session = HibernateUtil.currentSession();Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();yeeku.setName(new Name("crazyit.org", "疯狂Java联盟"));yeeku.setEmail("test@crazyit.org");yeeku.setPet(new Cat("Garfield", "黄色"));session.save(yeeku);tx.commit();HibernateUtil.closeSession();}}

public class HibernateUtil{public static final SessionFactory sessionFactory;static{try{//采用默认的hibernate.cfg.xml来启动一个Configuration的实例Configuration configuration = new Configuration().configure();//由Configuration的实例来创建一个SessionFactory实例sessionFactory = configuration.buildSessionFactory();}catch (Throwable ex){System.err.println("Initial SessionFactory creation failed." + ex);throw new ExceptionInInitializerError(ex);}}//ThreadLocal可以隔离多个线程的数据共享,因此不再需要对线程同步public static final ThreadLocal<Session> session= new ThreadLocal<Session>();public static Session currentSession()throws HibernateException{Session s = session.get();//如果该线程还没有Session,则创建一个新的Sessionif (s == null){s = sessionFactory.openSession();//将获得的Session变量存储在ThreadLocal变量session里session.set(s);}return s;}public static void closeSession()throws HibernateException {Session s = session.get();if (s != null)s.close();session.set(null);}}


原创粉丝点击