session与线程安全
Session不是线程安全的。Session中包含了数 据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱。每个线程/事务应该从一个SessionFactory获取自己的session实例,才能在新线程中使用事务和延迟加载等功能,否则会曝出no session异常。
获取session方式
1.sessionFactory.openSession
此方法每次都是新开一个session。并且在完成后需要手动close
2.sessionFactory.getCurrentSession
当hibernate.cfg.xml中配置了session线程绑定
<property name="current_session_context_class">thread</property>
getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭,若没有配置线程绑定,此方法报错
org.hibernate.HibernateException: No CurrentSessionContext configured!
事务Transaction
事务隔离级别配置
<!--
事务隔离级别
hibernate.connection.isolation = 4(默认值)
1—Read uncommitted isolation
2—Read committed isolation
4—Repeatable read isolation
8—Serializable isolation
-->
<property name="hibernate.connection.isolation">4</property>
1.如果不手动获取事务,则每次session的curd操作都会自动提交。
2.session.beginTransaction等同于session.getTransaction().begin;
3.一个session只允许同时存在一个begin的session.
4.当transaction.commit后,不能再次begin改事务实例,可以重新beginTransaction.
基本curd
save
Customer customer = new Customer();
customer.setCustName("测试保存");
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
session.save(customer);//此处虽然已经打印出insert语句,但是实际并未插入,事务未提交
tx.commit();
session.close();
get
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 1L);
System.out.println(customer);
tx.commit();
session.close();
get方法只能根据id去查询,所以事实上很少使用
update
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
// Customer customer = session.get(Customer.class, 1L);
Customer customer = new Customer();
customer.setCustId(95L);
customer.setCustName("啊啊啊");
session.update(customer);
tx.commit();
session.close();
update方法同样需要指定id,如果不存在,则会报错
delete
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCustId(7L);
session.delete(customer); //虽然delete方法需要entity做参数,但只需要指定entity的id就好
tx.commit();
session.close();
delete方法如果找不到此id,同样会报错。
load
Session session = HibernateUtil.openSession();
Transaction tx = session.beginTransaction();
Customer customer = session.load(Customer.class, 4L); //此处并不会有sql语句打印,说明还未查询数据库
Thread.sleep(5000);
System.out.println(customer); //此处才会查询数据库,有打印语句
tx.commit();
session.close();
load方法和get方法相似,都是查询一个值
区别在于
1.查询时机不一样
get方法任何时候都是立即加载(立即查询数据库)
load方法默认是延迟加载,在真正用到对象的非ID字段时才加载
load可以通过配置立即加载
2.返回结果不一样
get返回实体对象
load如果是延迟加载时,返回的是实体对象的代理
通过class标签上的load属性来设置是否延迟加载,默认是true
持久化类规范
除去一般javaBean要求的,私有属性,get/set方法,无参构造器外,hibernate的持久化类还有如下规则:
-
持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。举个例子:
假设表中有一列员工工资,如果使用double类型,如果这个员工工资忘记录入到系统中,系统会将默认值0存入到数据库,如果这个员工工资被扣完了,也会向系统中存入0.那么这个0就有了多重含义,而如果使用包装类类型就会避免以上情况,如果使用Double类型,忘记录入工资就会存入null,而这个员工工资被扣完了,就会存入0,不会产生歧义。
-
持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中是通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个OID相同的持久化对象的
-
持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么就不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。
主键生成策略
先区分自然主键和代理主键
自然主键
把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把name字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。维护和拓展性很差
代理主键
把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间
规则 | 描述 |
---|---|
increment | 用于int,long,short型,由hibernate自增生成id,当没有其他进程往数据库插入时才可用,故集群下不可用。用于代理主键 |
identity | 由底层数据库本身提供的主键生成id,前提是数据库支持自增长,db2,mysql,sqlserver均支持 |
sequence | 根据数据库序列生成id,前提是数据库支持序列,mysql不支持 |
naive | 根据数据库对自动生成符的能力来选择,identity,sequence,hilo。因此具有跨数据库能力,常用 |
hilo | 使用高低算法生成 short ,int 和 long 类型的 id 。需要一张额外的表保存 hi 的值。保存 hi 值的表至少有一条记录(只与第一条记录有关),否则会出现错 |
uuid | 采用128位的uui算法生成标志符,被编码成32位的16进制字符串。不流行,因为字符串类型比整数类型占更多空间 |
assigned | 程序必须手动指定id,否则报错,默认就是此策略 |
网友评论