封装HibernateSessionFactory
因为Session是线程不安全的,为了保证当前线程只有一个session对象,并且当前线程中的Session不能让其他线程来访问,需要将获取Session的方法进行封装,为了保证Session的线程安全性,需要将Session放到ThreadLocal中。
ThreadLocal为线程本地变量
封装过程:(非常重要)
public class HibernateSessionFactory { private static String configFile ="/hibernate.cfg.xml"; //声明一个线程本地变量,用来存放当前中的Session,保证不让其他线程访问到 private static ThreadLocal<Session> threadLocal = new ThreadLocal<>(); private static Configuration config = new Configuration(); private static SessionFactory sessionFactory; static { //读取配置文件 config.configure(configFile); //创建sessionFactory sessionFactory = config.buildSessionFactory(); } //获取session public static Session getSession() { /*当获取Session的时候先到当前线程的本地变量中寻找,如果有直接返回, *如果有直接返回,如果没有或者当前线程已经关闭需要重新开启一个Session *,返回,并且放到当前线程中 */ Session session = threadLocal.get(); if(session == null || !session.isOpen()){ session =(sessionFactory != null) ? sessionFactory.openSession() : null; threadLocal.set(session); } return session; } //关闭session public static void close(){ /* *关闭的时候先到当前线程中的本地变量中拿到session,如果可以取到,直接关闭 *最后将本地变量清空 */ Session session = threadLocal.get(); threadLocal.set(null); if(session!=null){ session.close(); } } } |
映射一对一关联
1)通过外键来映射
数据库表中的关系:一个中国公民对应一个身份证 | |
主表:d_card( Id cardNo );
| 从表: d_person( id name card_id references d_card(id) unique(card_id) ); 在从表中维护外键,外键指向主表的主键 |
类中的关系: 双向维护 | |
主类:Card{ Id cardNo Person person } | 从类:Person{ Id name Card card } |
映射关系上: | |
主方: card.hbm.xml <one-to-one name="person" class="Person" property-ref="card"/> | 从方:person.hbm.xml <many-to-one name="card" class="Card" column="card_id" unique="true"/> |
注意: unique="true" 是在使用hibernate的自动建表的时候才用到,用来指定表中的外键唯一,即给表中的外键添加唯一性约束,保证是一对一的关系 property-ref="" 用来指定关联类的属性名,这个属性将会和本类中的主键相对应,如果没有指定,默认使用关联类的主键和本类中的主键相对应。 在一对一关系中,如果没有在<one-to-one>中指定property-ref="card",当从主方对象寻找从方对象的时候将会使用自身的主键和关联类的主键匹配,这样就会出问题!如果指定后,将会用自身的主键和关联类的外键匹配。property-ref=""中的值为属性。 |
2)通过主键来映射
数据库表中的关系:一个中国公民对应一个身份证 | |
主表:d_card( Id cardNo );
| 从表: d_person( Id references d_card(id) name ); 从表的主键引用主表的主键 |
类中的关系: 双向维护 | |
主类:Card{ Id cardNo Person person } | 从类:Person{ Id name Card card } |
映射关系上: | |
主方: card.hbm.xml <one-to-one name="person" class="Person" /> | 从方:person.hbm.xml <id name="id"> <generator class="foreign"> <param name="property">card</param> </genetatory> </id> <one-to-one name="card" class="Card" /> |
注意: 从表中的主键引用了主表中的主键,所以需要在主键生成方式中设置成foreign,并将参数传进去,告诉hibernate应该使用哪一个主键。在配置上,双方都为one-to-one |
映射一对多关联
数据库表中的关系:一个顾客对应多个订单 | |
主表:d_customer( Id name );
| 从表: d_order( Id name c_id references d_customer(id) ); 从表的主键引用主表的主键 |
类中的关系: 双向维护 | |
主类 Customer{ Id name Set<Order> orders = new HashSet<>() } 一个顾客对应多个订单,所以在对象中需要用一个集合来维护,后期可以通过getter方法获取属于该顾客的所有订单 | 从类:Order{ Id name Customer customer } 一个订单只能属于一个顾客,所以需要维护一个顾客对象,后期通过getter方法获取该订单的所属者。 |
映射关系上: | |
主方: customer.hbm.xml <set name="orders"> <key column="c_id"></key> <one-to-many class="Order"/> </set> | 从方:order.hbm.xml <many-to-one name="customer" class="Customer" column="c_id" > |
注意: 在映射文件中,双方都维护了外键。主方通过<set>集合中的<key>来维护,从方通过 <many-to-one> 中的column来维护。
|
映射多对多关联
数据库表中的关系:一个人对应多种角色,一个角色对应多个人 | ||
主表:d_user( Id name );
| 从表: d_role( Id name );
| |
桥表:d_user_role( user_id references d_user(id), role_id references d_role(id). unique(user_id,role_id) ); 多对多的关系是维护在桥表上的,桥表中维护了两个外键,分别指向两个表的主键,并且这两个外键为联合主键。 | ||
类中的关系: 双向维护 | ||
主类 User{ Id name Set<User> roles= new HashSet<>() } 一个用户对应多个角色,用一个集合来维护 | 从类:Role{ Id name Set<User> users=new hashSet<>() } 一个角色对应多个人,需要用一个集合来维护 | |
映射关系上: | ||
主方: user.hbm.xml <set name="roles" table="d_user_role"> <key column="user_id"> < many-to-many class="Role" column="role_id"/> </set > | 从方:role.hbm.xml <set name="users" table="d_user_role"> <key column="role_id"> < many-to-many class="User" column="user_id"/> </set > | |
注意: 在映射元素上反应的是关联关系,由于多对多需要使用桥表来维护关联关系,所以需要在set元素中添加table元素用来指定桥表,key元素中的column属性表示本类在桥表中的外键。many-to-many表示对应的另外一方,其中的column属性表示对应的类在桥表中的外键。 | ||
继承映射
1每个类一张表
在表中的关系--子类的主键引用父类的主键,保证父子类的id一致 | ||
父类表--动物 animal( id, name ); | 子类表---狗 Dog( id references animal(id), type ); | 子类表---鸟 Bird( id references animal(id), type ); |
在类中的关系 | ||
Animal{ id, name } | Dog extends Animal{ type } | Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 | ||
<class name="Animal" table="d_animal"> <id name="id" column="id"> <generator class="increment"/> </id> <property name="name"/> <!-- 以上是配置父类的基本属性,需要指定父类使用的表名--> <!-- 以上是配置子类的配置,需要指定子类使用的表名,使用joined-subclass来和父类关联,key值继承父类的,column指定子类在表中的主键;property为子类特有的属性--> <joined-subclass name="Dog" table="h_dog"> <key column="id"></key> <property name="type"/> </joined-subclass> <joined-subclass name="Bird" table="h_bird"> <key column="id"></key> <property name="color"></property> </joined-subclass> </class> |
2一个类域一张表
在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段 | ||
Allclass( id name type color a_value --用来表示该条记录是属于哪个类的 ); | ||
在类中的关系 | ||
Animal{ id, name } | Dog extends Animal{ type } | Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 | ||
<class name="Animal" table="h_allclass"> <id name="id" column="id"> <generator class="increment"> </generator> </id> <!-- discriminator用来指定标识列 --> <discriminator column="a_value"/> <property name="name"/> <!-- discriminator-value用来指定标识列中应该填入的值,用这个值来区分这条记录是属于哪个类的,property用来指明子类特有的属性 --> <subclass name="Dog" discriminator-value="dog"> <property name="type"/> </subclass> <subclass name="Bird" discriminator-value="bird"> <property name="color"/> </subclass> </class> |
3每个子类一张表
在表中的关系--父子类共用一张表,这张表中包含了父子类的所有字段 | ||
| ||
在类中的关系 | ||
Animal{ id, name } | Dog extends Animal{ type } | Bird extends Animal{ color } |
在映射文件中的关系,配置在同一个映射文件中 | ||
<class name="Animal"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="color"/> <union-subclass name="Dog" table="dog"> <property name="callType"></property> </union-subclass> <union-subclass name="Bird" table="bird"> <property name="type"></property> </union-subclass> </class> | ||
主键生成方式不能使用Increment,因为Animal类并没有对应的表,可以使用序列 |