Hibernate基础

原创
2016/10/11 22:20
阅读数 30

1、hibernate基础

    1.1、hibernate快速预览

    Hibernate是目前非常流行的ORM(Object Relation Mapping)框架,通过ORM框架我们可以仅仅只用
    对对象进行操作就可以轻松的实现对数据库的操作。

    入门:(以下操作基于hibernate-release-5.2.3.Final)

    1、导入Hibernate的jar包

        hibernate-release-5.2.3.Final-->lib-->required下的所有包,log4j包,mysql-connector-java包。

    2、创建相应的业务对象(User)

    3、创建Hibernate的配置文件

        src-->hibernate.cfg.xml,在这个配置文件中进行相应的数据库连接设定     

<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<!-- hibernate的方言,用来确定连接的数据库 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- 数据库的连接类 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<!-- 数据库的连接字符串和用户名密码 -->
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/pm_hibernate?useSSL=false</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">123456</property>
		<!-- 在使用hibernate时会显示相应的SQL -->
		<property name="show_sql">true</property>
		<!-- 会自动完成类到数据表的转换 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 加入实体类的映射文件 -->
		<mapping resource="org/pm/hibernate/model/User.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

    4、为相应的业务对象创建xxx.hbm.xml配置文件,在这个文件中说明与数据库的映射关系,如果为User对
        象创建就在User对象所在的包中创建一个User.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 package="org.pm.hibernate.model">
    <class name="User" table="t_user">
        <id name="id">
            <generator class="native"/>
        </id>
		<property name="username"/>
		<property name="password"/>
		<property name="nickname"/>
		<property name="born" type="timestamp"/>
    </class>
</hibernate-mapping>

    5、将User.hbm.xml文件加入到hibernate.cfg.xml文件中。

		<!-- 在使用hibernate时会显示相应的SQL -->
		<property name="show_sql">true</property>
		<!-- 会自动完成类到数据表的转换 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 加入实体类的映射文件 -->
		<mapping resource="org/pm/hibernate/model/User.hbm.xml"/>

    6、写测试代码

		//1、新建Configuration对象  
        //Configuration cfg =new Configuration().configure();  
        //2、通过Confinguration创建SessionFactory对象  
        //在hibernate3.x是这种写法  
        //SessionFactory sf =cfg.buildSessionFactory();  
        //hibernate4.3  
        //  ServiceRegistry registry = new StandardServiceRegistryBuilder()  
        //  .applySettings(cfg.getProperties())  
        //  .build();  
        //  SessionFactory sf = cfg.buildSessionFactory(registry);  
        //3、通过SessionFactory对象得到Session  
		
		//hibernate-5.2.3创建SessionFactory
		//1. 配置类型安全的准服务注册类,这是当前应用的单例对象,不作修改,所以声明为final
	    //在configure("cfg/hibernate.cfg.xml")方法中,
		//如果不指定资源路径,默认在类路径下寻找名为hibernate.cfg.xml的文件
	    final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
	    		.configure("hibernate.cfg.xml").build();
	    //2. 根据服务注册类创建一个元数据资源集,
	    //同时构建元数据并生成应用一般唯一的的session工厂
	    SessionFactory sessionFactory = new MetadataSources(registry)
	    		.buildMetadata().buildSessionFactory();
	    //从会话工厂获取一个session
	    Session session = null;
	    try {
			session = sessionFactory.openSession();
			//开启一个新的事务
			//对于Hibernate而言,必须开启事务才能进行增加、删除、修改操作
			session.beginTransaction();
			User user = new User();
			user.setNickname("张三");
			user.setUsername("zhangsan");
			user.setPassword("123");
			user.setBorn(new Date());
			session.save(user);
			//提交事务
			session.getTransaction().commit();
		} catch (HibernateException e) {
			e.printStackTrace();
			//如果有异常需要回滚事务
			if(session!=null) session.getTransaction().rollback();
		} finally {
			if(session!=null) session.close();
		}

1.2、使用Hibernate实现CRUD

    1、编写一个HibernateUtil类,在这个类中统一创建单例的SessionFactory和Session

public class HibernateUtil {
	//单例
	private final static SessionFactory FACTORY = buildSessionFactory();
	
	//创建SessionFactory
	private static SessionFactory buildSessionFactory() {
		StandardServiceRegistry registry = null;
		SessionFactory factory = null;
		try {
			registry = new StandardServiceRegistryBuilder()
					.configure("hibernate.cfg.xml").build();
			//不指定文件名默认是找hibernate.cfg.xml文件
			//registry = new StandardServiceRegistryBuilder().configure().build();
			factory = new MetadataSources(registry)
					.buildMetadata().buildSessionFactory();
		} catch (Exception e) {
			e.printStackTrace();
			StandardServiceRegistryBuilder.destroy(registry);
		}
		return factory;
	}
	
	//获取SessionFactory
	/*public static SessionFactory getSessionFactory() {
		return FACTORY;
	} */
	
	//打开并返回一个Session
	public static Session openSession() {
		return FACTORY.openSession();
	}
	
	//关闭Session
	public static void closeSession(Session session) {
		if(session!=null) session.close();
	}

    2、编写测试方法

public class TestCRUD {
	private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
	
	@Test
	public void testAdd() {
		Session session = null;
		try {
			//通过HibernateUtil打开session
			session = HibernateUtil.openSession();
			session.beginTransaction();
			
			User u = new User();
			u.setBorn(sdf.parse("1988-08-18"));
			u.setNickname("老张");
			u.setPassword("123");
			u.setUsername("laozhang");
			session.save(u);
			
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			if(session!=null) session.getTransaction().rollback();
		} finally {
			HibernateUtil.closeSession(session);
		}
	}
	
	@Test
	public void testLoad() {
		Session session = null;
		try {
			/*long start = System.currentTimeMillis();
			System.out.println("start:"+start);*/
			session = HibernateUtil.openSession();
			//System.out.println("openSession1:"+(System.currentTimeMillis()- start)+"ms");
			//查询方法可以不需要事务
			User u = session.load(User.class, 4);
			System.out.println(u);
			//System.out.println("sql1:"+(System.currentTimeMillis()- start)+"ms");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}
		
		/*try {
			long start2 = System.currentTimeMillis();
			System.out.println("start2:"+start2);
			session = HibernateUtil.openSession();
			System.out.println("openSession2:"+(System.currentTimeMillis()- start2)+"ms");
			User u = session.load(User.class, 4);
			System.out.println(u);
			System.out.println("sql2:"+(System.currentTimeMillis()- start2)+"ms");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}*/
	}
	
	@Test
	public void testUpdate() {
		Session session = null;
		try {
			session = HibernateUtil.openSession();
			session.beginTransaction();
			User u = session.load(User.class, 2);
			u.setNickname("李四");
			session.update(u);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			if(session!=null) session.getTransaction().rollback();
		} finally {
			HibernateUtil.closeSession(session);
		}
	}
	
	@Test
	public void testDelete() {
		Session session = null;
		try {
			session = HibernateUtil.openSession();
			session.beginTransaction();
			User u = new User();
			u.setId(3);
			session.delete(u);
			session.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			if(session!=null) session.getTransaction().rollback();
		} finally {
			HibernateUtil.closeSession(session);
		}
	}
	
	@Test
	public void testList() {
		Session session = null;
		try {
			session = HibernateUtil.openSession();
			//查询一组数据需要使用HQL语句,需要创建相应的Query对象
			List<User> users = session.createQuery("from User",User.class).getResultList();
			for(User u:users) {
				System.out.println(u);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}
	}
	
	@Test
	public void testPager() {
		Session session = null;
		try {
			session = HibernateUtil.openSession();
			//查询分页列表,setFirstResult(0):设置起始页,setMaxResults(2):设置显示页数
			List<User> users = session.createQuery("from User",User.class)
						.setFirstResult(0)
						.setMaxResults(2).getResultList();
			for(User u:users) {
				System.out.println(u);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}
	}

1.3、Hibernate的状态

1、Transient(瞬时状态):即没有id,也没有被session管理。

			User u = new User();
			u.setBorn(sdf.parse("1999-09-09"));
			u.setNickname("赵四");
			u.setPassword("123");
			u.setUsername("zs");
			//以上u就是transient(瞬时状态),表示没有被session管理并且数据库中没有被保存

2、Persistent(持久化状态):当执行了save()、update()等操作之后,这个对象就会被session所管理,被
        管理之后就称为Persistent。

    持久化状态的对象是有id的。

			//执行save方法之后,被session所管理,而且数据库中已经存在,
			//此时就是persistent(持久化)状态
			session.save(u);
			//此时u是持久化状态,已经被session所管理,当在提交时,会把session中的对象和
			//目前的对象进行比较,如果两个对象中的值不一致就会继续发出相应的sql语句
			u.setNickname("赵四武");
			//此时会发出两条sql,一条用来做插入,一条用来做更新
			session.getTransaction().commit();
			User u = new User();
			u.setBorn(sdf.parse("1988-05-028"));
			u.setNickname("赵四六");
			u.setPassword("123");
			u.setUsername("zsl");
			session.save(u);
			u.setPassword("222");
			//该条语句没有意义
			session.save(u);
			u.setNickname("赵四流");
			//如果这个对象处于持久化状态,调用save,update没有任何意义
			session.update(u);
			u.setBorn(sdf.parse("1979-3-4"));
			//没有意义
			session.update(u);
			session.getTransaction().commit();
			User u = new User();
			u.setBorn(sdf.parse("1988-05-028"));
			u.setNickname("赵四六");
			u.setPassword("123");
			u.setUsername("zsl");
			session.save(u);
			/*
			 * 以下三条语句没有任何意义
			 * 当这个对象处于持久化状态的时候,如果值没有修改,调用save,update不会发出sql
			 */
			session.save(u);
			session.update(u);
			session.update(u);
			u.setUsername("zxq");
			//只要在事务提交之前发现u的值有修改就会发出sql语句
			session.getTransaction().commit();

3、Detached(离线状态):有id,但是没有被session所管理,(session关闭,创建一个对象,设置了
        相应的id)

			User u = new User();
			//此时u为离线状态,id存在,但是没有被session所管理
			u.setId(10);
			u.setNickname("abc");
			//当执行save()的时候总是会添加一条数据,此时id就会根据Hibernate所定义的规则来生成
			session.save(u);
			session.getTransaction().commit();
			User u = new User();
			u.setId(10);
			//完成update()之后也会变成持久化状态,又会被session所管理
			session.update(u);
			u.setBorn(sdf.parse("1978-12-23"));
			u.setNickname("哈哈");
			u.setPassword("123");
			u.setUsername("hh");
			//会发出一条sql
			session.update(u);
			session.getTransaction().commit();

4、对象状态的转换

5、几种特殊情况

5.1、如果对离线对象进行save操作,不会执行update而会直接insert。

			User u = new User();
			u.setId(10);
            //以上u是离线状态
			u.setNickname("abc");
			//对于离线状态的对象执行save时会忽略状态而直接进行插入操作
			session.save(u);
            //目前u是持久化对象,id为最新插入的值
            System.out.println(u.getId());
			session.getTransaction().commit();

5.2、对于瞬时对象而言,如果执行update会抛出异常(id找不到)

            User u = new User();
			u.setBorn(sdf.parse("1999-09-09"));
			u.setNickname("赵四");
			u.setPassword("123");
			u.setUsername("zs");
            //以上是瞬时状态,由于没有id,所以更新会报错
            session.update(u);

5.3、saveOrUpdate

			User u = new User();
//			u.setId(110);
			u.setUsername("abc");
			//如果u是离线状态就执行update操作,如果是瞬时状态就执行save操作
			//特别注意:该方法一般不会使用,因为我们在开发过程中是完全控制着数据对象状态
			session.saveOrUpdate(u);

5.4、一个session中只能存在一份id相同的持久化状态对象

			session = HibernateUtil.openSession();
			session.beginTransaction();
			//u1已经是持久化状态
			User u1 = session.load(User.class, 11);
			System.out.println(u1.getNickname());
			//u2是离线状态
			User u2 = new User();
			u2.setId(11);
			u2.setPassword("1234567890");
			//此时u2将会变成持久化状态,在session的缓存中就存在了两份同样的对象,
			//在session中不能存在两份拷贝,否则就会抛出异常
//			session.saveOrUpdate(u2);
			//merge方法会判断session中是否已经存在同一个对象,如果存在就将两个对象合并
			session.merge(u2);  //u变为持久化状态
			//最佳实践:merge一般不用
			session.getTransaction().commit();

1.4、延迟加载

    要获取一个对象有两种方法:get和load。

    ·对于get方法而言不支持延迟加载(LazyLoad(懒加载))。

		Session session = null;
		try {
			session = HibernateUtil.openSession();
			//get是只要一执行就会发出sql,get没有延迟加载
			User u = session.get(User.class, 1);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}

    ·对于load而言支持延迟加载,延迟加载指的是执行了load之后,并不会直接发出sql语句去数据库中取数
    据,只有在使用了这个对象的非id属性之后才会调用sql去数据库中取数据,这是一种优化的策略。

		Session session = null;
		try {
			session = HibernateUtil.openSession();
			User u = session.load(User.class, 1);
			//此时一条sql都没有发,这就是Hibernate的延迟加载
			/**
			 * 延迟加载指的就是,当完成load操作之后,并不会马上发出sql语句,只有在使用到
			 * 该对象时才会发出sql,当完成load之后,u其实是一个代理对象,
			 * 这个代理对象中仅仅只有一个id的值。
			 */
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}
		Session session = null;
		try {
			session = HibernateUtil.openSession();
			User u = session.load(User.class, 1);
			//此时一条sql都没有发,这就是Hibernate的延迟加载
			/**
			 * 延迟加载指的就是,当完成load操作之后,并不会马上发出sql语句,只有在使用到
			 * 该对象时才会发出sql,当完成load之后,u其实是一个代理对象,
			 * 这个代理对象中仅仅只有一个id的值。
			 */
			//此时不会发sql
			System.out.println(u.getId());
			//nickname在代理对象中没有值,必须去数据库中取。所以会发出sql。
			System.out.println(u.getNickname());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}

    ·使用load可能会出现的问题

	@Test
	public void testLazyQuestion() {
		UserDao ud = new UserDao();
		/*
		 * 由于使用了load,load是有延迟加载的,返回的时候的u是一个代理对象,仅仅只有一个id
		 * 但是在返回的时候session已经被关闭了,此时当需要使用u的其它属性时就需要去数据库中查找
		 * 但是session关闭了,所以就抛出org.hibernate.LazyInitializationException: 
		 * could not initialize proxy - no Session异常
		 */
		User u = ud.load(2);
		System.out.println(u.getNickname());
	}


public class UserDao {
	
	public User load(int id) {
		Session session = null;
		User u = null;
		try {
			session = HibernateUtil.openSession();
			u = session.load(User.class, id);
			//可以使用get方法解决延迟加载问题
//			u = session.get(User.class, id);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}
		return u;
	}
}

    ·一些特殊情况:

        get的特殊情况

		Session session = null;
		try {
			session = HibernateUtil.openSession();
			//get是只要一执行就会发出sql,get没有延迟加载
			User u = session.get(User.class, 101);
			//此时由于取了这个数据,发现数据库中并没有该数据(id为101的数据不存在),
			//所以u是null,所以会抛出空指针异常
			System.out.println(u.getId());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}

    load的特殊情况

		Session session = null;
		try {
			session = HibernateUtil.openSession();
			User u = session.load(User.class, 101);
			//由于u是一个代理对象,id已经存在,所以不会抛出异常
			System.out.println(u.getId());
			//此时会去数据库中取数据,发现没有这个对象(id为101),但是u并不是空,
			//所以会抛出ObjectNotFoundException异常
			//特别注意:不是NullPointException异常
			System.out.println(u.getNickname());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession(session);
		}

    可以通过设置xxx.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 package="org.pm.hibernate.model">
	<!-- lazy="false"表示取消延迟加载 -->
    <class name="User" table="t_user" lazy="false">
        <id name="id">
            <generator class="native"/>
        </id>
		<property name="username"/>
		<property name="password"/>
		<property name="nickname"/>
		<property name="born" type="timestamp"/>
    </class>
</hibernate-mapping>

1.5、ID生成策略

    id是每一个对象必须有的标识,一般情况都是用一个没有任何语义的字段作为id。

        <id name="id">
            <generator class="native"/>
        </id>

    generator表示可以设置id的生成策略,对于MySQL而言:auto_increment,对于oracle而言:是使用
    sequence来生成自动递增序列的。

    native表示会自动根据数据库进行选择,如果是MySQL就选择auto_increment,如果是oracle就选择
    sequence。

    对于native而言id必须是int类型。

			session = HibernateUtil.openSession();
			session.beginTransaction();
			Book b = new Book();
			b.setName("Think in Java");
			b.setPrice(128.8);
			session.save(b);
			//此时就会发sql,id是在数据库插入数据时自动生成的
			session.getTransaction().commit();

    还有另外一种生成策略:uuid(会自动生成一个字符串,如果使用uuid主键必须是varchar类型)

        <id name="id">
            <!-- 会自动生成一个字符串,此时主键必须为String类型 -->
            <generator class="uuid"/>
        </id>

    还可以设置生成策略为assigned,assigned表示主键由用户自己指定(不经常用)

        <id name="id">
        	<!-- assigned表示不会自动生成,而是需要由用户来指定 -->
            <generator class="assigned"/>
        </id>
			session = HibernateUtil.openSession();
			session.beginTransaction();
			Book b = new Book();
			b.setName("Think in Java");
			b.setPrice(128.8);
            //第一次添加时id为0不会报错
			session.save(b);
			//第二次运行save就会抛出主键重复异常,因为id依然为0,而id需要由开发人员自己指定
			session.getTransaction().commit();

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部