Hibernate Criteria

除了hql意外,hibernate可以通过对象的查询条件方式查询

public List<Users> getUsersByCritetia(String name){
		Criteria criteria = HibernateUtil.getSession().createCriteria(Users.class);
		criteria.add(Restrictions.like("name", "%" + name + "%"));
		
		criteria.setFirstResult(0);
		criteria.setMaxResults(10);
		return (List<Users>)criteria.list();
	}

Query里面有的查询方法criteria都有,通过Restrictions来设置查询条件,只是如果有复杂的联合查询,Criteria可能无法实现,只能用HQL了。

Hibernate hql

String hql = "from Users";
		Query query = HibernateUtil.getSession().createQuery(hql);
		List<Users> list = query.list();
		// query.uniqueResult()

list:取得对象list,如果查询的对象有子类,会检索所有的子类

uniqueResult:取得唯一的对象,如果实际查询返回多条的时候会抛异常

hql带参查询方式:

String hql = "from Users as users where users.name = :name";
....
query.setString("name", ***);

分页:
query.setFirstResult(0);
query.setMaxResults(10);

因为我们知道不同的数据库分页的语句不通,比如mysql是limit …,sqlserver是top…

hibernate通过<property name=”dialect”>org.hibernate.dialect.MySQLDialect</property>在MySQLDialect这个类里面根据不同的数据库选择不同的分页方式

 

Hibernate 实体的三种状态

以Hibernate为参照物,对象的三种状态:

  • 瞬时:对象在数据库里面不存在数据,也没有被Session管理,这种状态就是瞬时,比如new Users()这个对象就是瞬时的。
  • 持久化:对象在数据库里面存在数据,也在被Session管理,对象值的改变能被hibernate知道,
  • 脱管(也叫游离态):对象在数据库里面存在数据,但是已经没有被Session管理了,比如session取出的对象,但是session已经关闭了

hibernate objects status

 

UsersDAO userDao = new UsersDAO();
			Users user = new Users();
			user.setName("ttt3");
			user.setAge(12);
			user.setAddTime(new Date());
			userDao.save(user);
			System.out.println(user.getId()); // 这里的user是脱管状态

public void save(Users user){
		Session session = null;
		Transaction tx = null;
		try{
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.save(user);
			user.setName("new name"); // 这里的user是持久化状态
			tx.commit();
		}finally{
			if(session != null){
				session.close();
			}
		}
	}

这个方法会触发两个操作,第一个是保存user,第二个是用”new name”更新user对象,当执行到tx.commit()的时候才进行更新操作,这样如果有多个值改变的情况不会经常的去访问数据库

从这两个例子可以看出

  • 当对象处于瞬时的状态的时候用save或者persist让对象持久化
  • 当对象处于脱管的状态使用update方法
  • 无法判断对象是瞬时还是脱管状态的时候使用saveOrUpdate方法,saveOrUpdate调用后对象就会持久化,另外一个更新方法merge也可以实现更新,但是merge调用后,对象还是脱管状态

saveOrUpdate方法:

hibernate内部判断如果主键是int,如果不等于0就update,等于0就save;如果主键是String,如果等于null就save,不等于null就update

在映射文件中id有一个属性unsaved-value,可以给他设置一个数值,比如-1,那么hibernate就会判断如果不是-1的时候就更新,是-1的时候就save。

 Object状态流转图:

hibernate_object_status_exchange

 

Hibernate load和get的区别

假设要取的对象是Users

load:

  1. 运用的是懒加载的模式,当用load取得一个Users对象,这个对象通过user.getClass().getName()得到的类型是Users$$EnhancerByCGLIB$$660971c2这样的一个Users的代理类,也即是Users的一个子类,这个类里面的数据属性全部都还没有从数据库取值。只有用user.getName()这样取值的时候才真正的访问数据库,但是这个user对象指向的是Users$$EnhancerByCGLIB$$660971c2这个类型的实例,user里面的所有属性还是全部为空,包括name属性
  2. load方法取不到值的时候会抛ObjectNotFound的异常

get:

  1. 用get方法取得Users对象是,他的类型就是Users class,而且会取出所有属性的值,
  2. get方法取不到值的时候,返回null。

Hibernate的概念和最简单例子

Hibernate的由来:

主要针对数据访问层,因为数据库是关系型的,而java是面向对象,所以这样的两种模型是不匹配的,需要进行数据转换,我们以前用的JDBC就是

  • 一方面把java对象的数据通过jdbc的api的转换转为数据库里面的数据
  • 一方面是把数据库里面的数据取出去,在Resultset里面把数据取出去放到java对象里面去

在对象很复杂的时候,两个模型之前的转换就有很多苦力工作需要做,hibernate这种ORM类的工具出现的一个目的就是让Object到关系数据的mapping更加的简便。

Hibernate重要的两个配置文件是映射文件(Users.hbm.xml)和配置文件(hibernate.cfg.xml)

配置文件:

<hibernate-configuration>
	<session-factory>
	    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
	    <property name="connection.url">jdbc:mysql://localhost:3306/test?useUnicode=true</property>
		<property name="connection.username">root</property>
		<property name="connection.password">111111</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		
		
		<mapping resource="com/practise/hibernate/bean/Users.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

主要分两部分:数据库连接配置和映射文件加载

可以在property里面加上这个属性:

hibernate.hbm2ddl.auto:

  • create-drop:每次执行的时候,如果没有找到映射文件里面的对应数据表,就自动创建,完成后自动删除表,测试的时候可以用这种模式
  • create:每次执行的时候先删除所有表,然后创建新表
  • update:不新建,只验证并更新表
  • validate:只验证表,如果表不存在,报异常(实际开发可用这个)

hibernate.cfg.xml和hibernate.properties如果两个都配置的话,两个都会自作用,是一个合集的效果,相同属性xml里面的属性配置生效。Configuration.configure()这个方法执行的时候会读取你的默认叫hibernate.cfg.xml的配置文件,如果是其他的名字,需要传Configuration.configure(othername.xml)参数进去。在配置文件中配置的属性,在代码里面也可以配置:

cfg.setProperty(key, value);

映射文件:

<hibernate-mapping 
	package="com.practise.hibernate.bean">

	<class name="Users" table="users">
		<comment>Users may bid for or sell auction items.</comment>
		
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"></property>
		<property name="age"></property>
		<property name="addTime"></property>
		
	</class>
	
</hibernate-mapping>

  • table属性可以不加,这样hibernate会找与name相同的数据表名,property的column默认不加的情况也是会寻找与name相同的数据表里面的列名
  • Users类如果没有默认构造方法(比如添加了带参构造方法,没有再添加不带参的),hibernate内部在用反射实例化的时候会出错
  • 表为多重主键的情况,用,但是在数据库设计的时候应该尽量避免多重主键
  • 尽量避免用数据库的关键字,否则有些数据库可能有异常,比如Oracle,如果有关键字的情况,在不修改表名的情况避免异常的方法是加上两个反向单引号,但数据库设计的时候表名也最好不要用关键字

public class BaseDAO {
	public Session getSession(){
		Configuration cfg = new Configuration();
		cfg.configure();
		SessionFactory factory = cfg.buildSessionFactory();
		return factory.openSession();
	}
}

public class UsersDAO extends BaseDAO {
	public void save(Users user){
		Session session = super.getSession();
		Transaction tx = session.beginTransaction();
		session.save(user);
		tx.commit();
		session.close();
	}
}

因为mysql5以上的版本默认是InnoDB这种类型,支持事务提交,回滚,在JDBC里面默认是自动提交的,但是在Hibernate里面默认是不提交的,需要显示的用启动Transation并提交。

删除方法:

session.delete(entity);

这个entity通常最好是查询出来的游离对象,如果new User(),然后为这个瞬时user设置一个数据库存在的id,同样可以删除成功。但是如果映射文件其他属性not-null=“true”那么其他的属性不能为空。