现在的位置: 首页 > 综合 > 正文

Hibernate学习笔记

2013年10月12日 ⁄ 综合 ⁄ 共 14826字 ⁄ 字号 评论关闭

一、所需的jar包(http://download.csdn.net/detail/huangzebiao007/6359911)

二、hibernate入门实例

1、通过myeclipse自动生成HibernateSessionFactory类和hibernate.cfg.xml配置文件(使用mysql数据库),

mysql驱动包的下载地址(http://download.csdn.net/detail/huangzebiao007/6359931)

package com.hzb.factory;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;

/**
 * Configures and provides access to Hibernate sessions, tied to the
 * current thread of execution.  Follows the Thread Local Session
 * pattern, see {@link http://hibernate.org/42.html }.
 */
public class HibernateSessionFactory {

    /** 
     * Location of hibernate.cfg.xml file.
     * Location should be on the classpath as Hibernate uses  
     * #resourceAsStream style lookup for its configuration file. 
     * The default classpath location of the hibernate config file is 
     * in the default package. Use #setConfigFile() to update 
     * the location of the configuration file for the current session.   
     */
    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
	private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    private  static Configuration configuration = new Configuration();    
    private static org.hibernate.SessionFactory sessionFactory;
    private static String configFile = CONFIG_FILE_LOCATION;

	static {
    	try {
			configuration.configure(configFile);
			sessionFactory = configuration.buildSessionFactory();
		} catch (Exception e) {
			System.err
					.println("%%%% Error Creating SessionFactory %%%%");
			e.printStackTrace();
		}
    }
    private HibernateSessionFactory() {
    }
	
	/**
     * Returns the ThreadLocal Session instance.  Lazy initialize
     * the <code>SessionFactory</code> if needed.
     *
     *  @return Session
     *  @throws HibernateException
     */
    public static Session getSession() throws HibernateException {
        Session session = (Session) threadLocal.get();

		if (session == null || !session.isOpen()) {
			if (sessionFactory == null) {
				rebuildSessionFactory();
			}
			session = (sessionFactory != null) ? sessionFactory.openSession()
					: null;
			threadLocal.set(session);
		}

        return session;
    }

	/**
     *  Rebuild hibernate session factory
     *
     */
	public static void rebuildSessionFactory() {
		try {
			configuration.configure(configFile);
			sessionFactory = configuration.buildSessionFactory();
		} catch (Exception e) {
			System.err
					.println("%%%% Error Creating SessionFactory %%%%");
			e.printStackTrace();
		}
	}

	/**
     *  Close the single hibernate session instance.
     *
     *  @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);

        if (session != null) {
            session.close();
        }
    }

	/**
     *  return session factory
     *
     */
	public static org.hibernate.SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/**
     *  return session factory
     *
     *	session factory will be rebuilded in the next call
     */
	public static void setConfigFile(String configFile) {
		HibernateSessionFactory.configFile = configFile;
		sessionFactory = null;
	}

	/**
     *  return hibernate configuration
     *
     */
	public static Configuration getConfiguration() {
		return configuration;
	}

}
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<!-- Generated by MyEclipse Hibernate Tools.                   -->
<hibernate-configuration>

    <session-factory>
        <property name="dialect">
            org.hibernate.dialect.MySQLDialect
        </property>
        <property name="connection.url">
            jdbc:mysql://localhost:3306/hibernate?useUnicode=true&amp;characterEncoding=UTF-8&amp;connectTimeout=3000&amp;socketTimeout=10000
        </property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!--<mapping resource="com/hzb/po/TMessage.hbm.xml" />-->

    </session-factory>

</hibernate-configuration>

2、使用myeclipse生成po类和*.hbm.xml映射文件,并在hibernate.cfg.xml配置该映射文件,把上面注释的部分去掉。

package com.hzb.po;

/**
 * TMessage entity. @author MyEclipse Persistence Tools
 */

public class TMessage  implements java.io.Serializable {


    // Fields    

     private Integer id;
     private String name;


    // Constructors

    /** default constructor */
    public TMessage() {
    }

	/** minimal constructor */
    public TMessage(Integer id) {
        this.id = id;
    }
    
    /** full constructor */
    public TMessage(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

   
    // Property accessors

    public Integer getId() {
        return this.id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

}
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="com.hzb.po.TMessage" table="t_message" catalog="hibernate">
        <id name="id" type="java.lang.Integer">
            <column name="id" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="name" length="20" />
        </property>
    </class>
</hibernate-mapping>

3、编写BaseDAO和DAO层代码

package com.hzb.dao;

import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

import com.hzb.factory.HibernateSessionFactory;


/**
 * 所实现的DAO类必须继承BaseDAO, BaseDAO提供了一组常用的持久方法.<br>
 * 在使用时只需要直接调用即可.
 * 
 */
public abstract class BaseDAO {

	/**
	 * 将对象持久化到数据库中. 此对象的第一个参数是Hibernate session, 其作用是在业务层中使<br>
	 * 用一个事务进行多个操作时使用.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param transientInstance - 持久化对象.
	 */
	public void save(Session session, Object transientInstance) {
		session.save(transientInstance);
	}

	/**
	 * 更新方法. 此对象的第一个参数是Hibernate session, 其作用是在业务层中使<br>
	 * 用一个事务进行多个操作时使用.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param detachedInstance - 持久化对象.
	 * @return - Object 更新后的持久化对象.
	 */
	public Object merge(Session session, Object detachedInstance) {
		return session.merge(detachedInstance);
	}
	
	/**
	 * 更新方法. 此对象的第一个参数是Hibernate session, 其作用是在业务层中使<br>
	 * 用一个事务进行多个操作时使用.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param detachedInstance - 持久化对象.
	 */
	public void update(Session session, Object detachedInstance) {
		session.update(detachedInstance);
	}
	
	/**
	 * 删除方法. 此对象的第一个参数是Hibernate session, 其作用是在业务层中使<br>
	 * 用一个事务进行多个操作时使用.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param persistentInstance - 持久化对象.
	 */
	public void delete(Session session, Object persistentInstance) {
		session.delete(persistentInstance);
	}
	
	/**
	 * 根据主键(id)进行查找一条数据. 此对象的第一个参数是Hibernate session, <br>
	 * 其作用是在业务层中使用一个事务进行多个操作时使用.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param className - 要查找的Class name(类全名).
	 * @param id - 要查找的主键(id).
	 * @return - 返回一个Object对象, 在使用方需要转型.
	 */
	public Object get(Session session, Class<?> clazz, long id) {
		return session.get(clazz, id);
	}
	
	/**
	 * 根据给定的HQL查询.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param hql - 查询的HQL语句.
	 * @return - List集合.
	 */
	public List<?> get(Session session, String hql) {
		return session.createQuery(hql).list();
	}
	
	/**
	 * 根据给定的HQL和分页数查询.
	 * 
	 * @param session - 需要进行事务操作的Session对象.
	 * @param hql - 查询的HQL语句.
	 * @param start - 开始条数.
	 * @param end - 结束条数.
	 * @return - List集合.
	 */
	public List<?> get(Session session, String hql, int start, int end) {
		Query query = session.createQuery(hql);
		query.setFirstResult(start);//起始记录编号,下标从0开始
		query.setMaxResults(end);//查询总条数
		return query.list();
	}

	/**
	 * 在SessionFactory中获取Session对象.
	 * 
	 * @return - Hibernate Session对象.
	 */
	public Session getSession() {
		return HibernateSessionFactory.getSession();
	}
	
	/**
	 * 在SessionFactory中关闭Session对象.
	 */
	public void closeSession() {
		HibernateSessionFactory.closeSession();
	}
}

package com.hzb.dao;

import java.util.List;

import org.hibernate.Session;

public class MessageDAO extends BaseDAO {
	public List<?> getAllMessages(Session session){
		return get(session,"from TMessage");
	}
	public List<?> getMessage(Session session, String name){
		return get(session,"from TMessage where name = '"+name+"'");
	}
}

4、编写business层代码

package com.hzb.business;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.hzb.dao.MessageDAO;
import com.hzb.po.TMessage;

public class MessageBusiness {
	public List<TMessage> getMessages() {
		Session session = null;
		List<TMessage> messages = null;
		try {
			MessageDAO messageDAO = new MessageDAO();
			session = messageDAO.getSession();
			messages = (List<TMessage>) messageDAO.getAllMessages(session);
			if (null != messages && messages.size() > 0) {
				System.out.println("有数据");
			} else {
				System.out.println("没有数据");
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != session)
					session.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return messages;
	}

	public void deleteMessage(String name) {
		Session session = null;
		Transaction tran = null;
		List<TMessage> messages = null;
		try {
			if (null != name && !"".equals(name)) {
				MessageDAO messageDAO = new MessageDAO();
				session = messageDAO.getSession();
				tran = session.beginTransaction();
				messages = (List<TMessage>) messageDAO
						.getMessage(session, name);
				if (null != messages && messages.size() > 0) {
					for (int i = 0, j = messages.size(); i < j; i++) {
						messageDAO.delete(session, messages.get(i));
					}
					tran.commit();
				} else {
					System.out.println("没有数据");
				}
			} else {
				System.out.println("参数错误");
			}

		} catch (Exception e) {
			if (null != tran)
				tran.rollback();
			e.printStackTrace();
		} finally {
			try {
				if (null != session)
					session.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

5、编写service层代码

package com.hzb.action;

import com.hzb.business.MessageBusiness;

public class MessageAction {

	public static void main(String[] args) {
		MessageBusiness mb = new MessageBusiness();
//		List<TMessage> messages = mb.getMessages();
//		for(TMessage message : messages){
//			System.out.println(message.getId()+":"+message.getName());
//		}
		mb.deleteMessage("hzb");
	}

}

三、防sql注入

上面dao层没有防止sql注入,一般要防止sql注入的话,我们不用拼接字符串这种形式,而是采用参数化。

有两种办法,一种是用 ? 来占位,一种是用  :参数名 来占位

1、用 ? 来占位:(参数名是从0开始)

上面的baseDAO中添加一个方法:

	public List<?> get(Session session, String hql, int[] attrs, Object[] values) {
		if (null != attrs && null != values &&
			attrs.length == values.length) {
			Query query = session.createQuery(hql);
			for (int i = 0, j = attrs.length; i < j; i++)
				query.setParameter(attrs[i], values[i]);
			return query.list();
		} else {
			return null;
		}
	}

而DAO类中改为:

	public List<?> getMessage(Session session, String name){
		return get(session,"from TMessage where name = ?",new int[]{0},new Object[]{name});
	}

2、用  :参数名 来占位:

上面的baseDAO中添加一个方法:

	public List<?> get(Session session, String hql, String[] attrs, Object[] values) {
		if (null != attrs && null != values &&
			attrs.length == values.length) {
			Query query = session.createQuery(hql);
			for (int i = 0, j = attrs.length; i < j; i++)
				query.setParameter(attrs[i], values[i]);
			return query.list();
		} else {
			return null;
		}
	}

而DAO类中改为:

	public List<?> getMessage(Session session, String name){
		return get(session,"from TMessage where name = :name",new String[]{"name"},new Object[]{name});
	}

四、多表联合查询,采用select new User(u.id,u.name,u.age,u.time,d.depName,d.depTest) from User u,Department d where u.depId = d.depId

1、在User类中加入联合查询的对象要显示的字段,并得到get,set方法

2、在User类中加入一个构造方法,跟new User(****)里面的字段一一对应。

3、HQL语句写成上面这种形式

五、sql语句的查询,sql语句查询得到List后要迭代,并用个对象数组来封装每一次迭代获取到的元素

public List<User> getAllUser3() {
		// TODO Auto-generated method stub
		Session s = HibernateSessionFactory.getSession();
		Transaction ts = s.beginTransaction();
		Query query = s.createSQLQuery(
		"select u.id,u.name,u.age,u.time,d.depName,d.depTest from user u left join department d on u.depId = d.depId");
		//Query query = s.createSQLQuery("select u.id,u.name,u.age,u.time,d.depName,d.depTest from user u ,department d where u.depId = d.depId");
		
		query.setFirstResult(2);//起始记录编号   
                query.setMaxResults(2);//此次查询总条数
		List<User> list = query.list();
		List<User> arrlist = new ArrayList<User>();
		Iterator it=list.iterator();
		while(it.hasNext()){
			Object[] objs=(Object[])it.next();
			User user = new User();
			user.setId(Integer.parseInt(objs[0].toString()));
			user.setName(objs[1].toString());
			if(objs[2]!=null){
				user.setAge(Integer.parseInt(objs[2].toString()));
			}else{
				user.setAge(1);
			}
			if(objs[3]!=null){
				user.setTime(objs[3].toString());
			}else{
				user.setTime("11111111");
			}
			if(objs[4]!=null){
			user.setDepName(objs[4].toString());
			}
			if(objs[5]!=null){
			user.setDepText(objs[5].toString());
			}
			arrlist.add(user);

		}
		ts.commit();
		s.close();
		return arrlist;
	}

public List<User> getAllUser2() {
		// TODO Auto-generated method stub
		Session s = HibernateSessionFactory.getSession();
		Transaction ts = s.beginTransaction();
		Query query = s.createSQLQuery("select * from user");
		query.setFirstResult(2);//起始记录编号   
                query.setMaxResults(3);//此次查询总条数
		List<User> list = query.list();
		List<User> arrlist = new ArrayList<User>();
		Iterator it=list.iterator();
		while(it.hasNext()){
			Object[] objs=(Object[])it.next();
			User user = new User();
			user.setId(Integer.parseInt(objs[0].toString()));
			user.setName(objs[1].toString());
			if(objs[2]!=null){
				user.setAge(Integer.parseInt(objs[2].toString()));
			}else{
				user.setAge(1);
			}
			if(objs[3]!=null){
				user.setTime(objs[3].toString());
			}else{
				user.setTime("11111111");
			}
			arrlist.add(user);

		}
		ts.commit();
		s.close();
		return arrlist;
}

六、常用的方法

1、get(Class,id)和load(Class,id)方法,根据ID获取一个对象

两者的区别:load是延迟加载,即只有使用到他的数据,比如getXxx()方法,他才会去查找,而get不是。

get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;而load方法首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。

get方法获取不到数据时返回null,而load方法获取不到时会抛出异常。一般都是用get方法

2、delete(Object)删除对象方法

3、save(Object),persist(Object)保存对象方法,一般用save方法

主要的区别在没有事务提交时,save会产生insert语句,而persist不会产生insert语句。但都不会插入数据库,因为save没有事务提交时虽然有插入语句,但会发生回滚。

4、update(Object)更新对象

5、saveOrUpdate(Object),merge(Object)方法,保存或更新方法,如果操作的对象的主键Id在数据库中已经有了,则更新对象,如果没有,则保存对象。

6、createQuery(hql)方法,hql语句查询

7、createSQLQuery(sql)方法,sql语句查询

七、缓存(主要是针对get,load,list,iterate方法),一般分为一级缓存,二级缓存,和查询缓存(基于二级缓存的)

1、一级缓存,Session级共享。
    save,update,saveOrUpdate,load,get,list,iterate这些方法都会将对象放在一级缓存中,一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用flush,clear方法先更新再清除缓存中的内容。(list会向缓存放数据,但不会向缓存中取数据)

2、二级缓存,SessionFactory级共享。(默认是打开的,他是用自己内部的缓存,但一般我们会用第三方的缓存)

跟上面一样,session的API方法都会填充二级缓存,但在没打开查询缓存时,只有Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询),list方法不会从二级缓存中取数据,只有配置了查询缓存,list方法才会从查询缓存中取数据。

3、list查询和iterate查询的区别

list查询是将所有的对象都查询出来,再放到缓存中;而iterate是先将所有的id查询出来,再根据id去缓存找数据,找不到数据再从数据库中找,找完后再放到缓存,所以如果开始时候缓存中没数据时,用iterate查询就会先发一条查询所有ID的语句,再根据每个id执行一条语句,也就是所谓的N+1问题(但如果是第二次再iterate的话,就只会发送一条查询id的语句了,因为缓存中数据在第一次iterate查询时候就已经放进去了,相比list的每次查询都把所有对象都查出来效率要高,一般情况我们是先用list执行第一次查询,查出所有对象放进缓存,再用iterate的话就每次只会查询id了)。

4、查询缓存

主要是针对list方法的,默认情况下关闭,需要打开。

查询缓存只对query.list()起作用,对query.iterate不起作用

5、配置二级缓存和查询缓存示例,该示例是用于一张初始化数据的表,表里面的数据一般不会改动,所以权衡了下,用了查询缓存,只是第一次list查询出了所有的对象,接着以后每次调用时都不会在查询数据库。

1)配置hibernate.cfg.xm文件:

		<!-- 启用二级缓存  -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<!-- 启用查询缓存  --> 
                <property name="cache.use_query_cache">true</property> 
                <!-- 指定第三方二级缓存提供商 -->
		<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

2)增加ehcache.xml文件

<ehcache>
    <!-- 设置当缓存对象益出时,对象保存到磁盘时的保存路径。
            如 d:\xxxx
         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - windows的临时目录 -->
         
    <diskStore path="java.io.tmpdir"/>

    <!--默认配置/或对某一个类进行管理
        maxElementsInMemory    - 缓存中可以存入的最多个对象数 
        eternal                - true:表示永不失效,false:不是永久有效的。 
        timeToIdleSeconds      - 空闲时间,当第一次访问后在空闲时间内没有访问,则对象失效,单位为秒 
        timeToLiveSeconds      - 被缓存的对象有效的生命时间,单位为秒 
        overflowToDisk         - 当缓存中对象数超过核定数(益出时)时,对象是否保存到磁盘上。true:保存;false:不保存
        					              如果保存,则保存路径在标签<diskStore>中属性path指定 -->
       
    <defaultCache
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="36000"
        timeToLiveSeconds="360000"
        overflowToDisk="true"
        />	
</ehcache>

3)在对应的*.hbm.xml文件的class标签下添加<cache usage="read-only"/>

备注:如果只是要用到二级缓存,到上面为止就配置完了(把查询缓存改为false)。

4)在调用list方法前设置setCacheable为true

	public List<?> getCache(Session session, String hql) {
		Query query = session.createQuery(hql);
		query.setCacheable(true); 
		return query.list();
	}

6、个人总结:session中的API方法都会把对象放入一级、二级缓存中,但只有get,load,iterate方法会从缓存中取数据,单纯的用iterate第一次查询时会造成N+1次问题,所以就有了另一种选择,针对list的查询缓存,使用了查询缓存后,list方法也就能从缓存中取数据,并且不会有N+1次问题。

抱歉!评论已关闭.