一、所需的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&characterEncoding=UTF-8&connectTimeout=3000&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次问题。