本节代码点击这里:https://github.com/MarsOu1998/HibernateWeb
1.Hibernate的基本开发流程
Configuration这个类先去读取配置文件,其实在这个类的源码中可以看见,hibernate.cfg.xml是默认的配置文件,所以读取时不需要指定。然后用Configuration这个类生成一个SessionFactory,然后SessionFactory打开一个session,最后根据增删改查,判断是否需要生成事务,然后提交。
2.Configuration类
Configuration负责管理Hibernate的配置文件,在配置文件里面又注册了映射文件(*.hbm.xml),所以Configuration是可以管理两个配置文件的。
从源码中可以看到,配置文件是默认的。
也可以通过在configure里面指定文件名字修改读取的配置文件
3.SessionFactory
应用程序从SessionFactory里获得Session实例,一般情况下,一个数据库只有一个SessionFactory,例如在应用被初始化的时候被创建,因为SessionFactory比较耗费内存。SessionFactory,也就是会话工厂,里面缓存了SQL语句和Hibernate在运行时使用的映射元数据。
SessionFactory目的是为每一个客户生成链接,所以放在application里面比较合适。
4.Session
Session通过SessionFactory打开,在所有的工作完毕之后,需要关闭。
它与Web层的HttpSession没有任何关系。
我们可以自己编写Session类,对日常利用Hibernate进行增删改查的代码做一些优化:
util.HibernateSessionFactory.java:
package util;
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;
}
}
查询:
优化前的查询代码:
TEST.Query1.java:
package TEST;
import PO.Student;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Query1 {
public static void main(String[] args) {
Configuration configuration=new Configuration().configure();
SessionFactory sessionFactory=configuration.buildSessionFactory();
Session session=sessionFactory.openSession();
Student student=new Student();
session.load(student,"oushile2");
System.out.println(student.getName());
System.out.println(student.getSex());
session.close();
}
}
优化后的查询代码:
TEST.Query1.java:
package TEST;
import PO.Student;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class Query1 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
Student student=new Student();
session.load(student,"oushile2");
System.out.println(student.getName());
System.out.println(student.getSex());
util.HibernateSessionFactory.closeSession();
}
}
效果是一样的。
更新:
在更新操作里面需要用到事务,最好是利用try和catch包围代码,和平时JDBC的链接风格类似。
Update1.java:
package TEST;
import PO.Student;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Update1 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
Student student=new Student();
session.load(student,"oushile2");
student.setSex("男");
Transaction transaction=session.beginTransaction();
try{
session.update(student);
transaction.commit();
}catch (Exception e){
transaction.rollback();
}finally {
util.HibernateSessionFactory.closeSession();
}
}
}
Session的一些API
保存的方法
- save,如果主键冲突会报错
- saveOrUpdate,如果主键重复了,会覆盖原先的数据。
不存在会抛出异常的方法
- load,如果查找的主键不存在或者多余1个,控制台会显示出异常
不存在不会抛出异常但是返回值为空的方法
- get方法,查找的主键没找到,但是如果需要打印或者做其他的操作会返回空指针异常。
修改
- update
删除
- delete
4.批量操作
批量查询——HQL,Hibernate查询语言
批量查询学生信息中年龄小于18的人:
Query2.java:
package TEST;
import PO.Student;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class Query2 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
String sql="from Student where age<18";
Query query=session.createQuery(sql);
List list=query.list();
for (int i = 0; i <list.size() ; i++) {
Student student=(Student)list.get(i);
System.out.println(student.getName()+" "+student.getSex());
}
util.HibernateSessionFactory.closeSession();
}
}
第一次接触的时候,就很奇怪,Hibernate不是号称毫无SQL痕迹的吗?怎么还是需要动手编写SQL语句呢。
原来这里的SQL语句和数据库没有关系,而是与配置文件有关系,这里数据库的属性名字都是根据*.hbm.xml的。
也就是说,如果数据库发生了迁移,开发者是不需要修改类内的源代码的,只需要在配置文件里面改一下映射关系即可。
精确批量查询
批量查询学生信息中年龄小于18的人,只挑选其中的姓名和性别属性:
Query3.java:
package TEST;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class Query3 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
String sql="select name,sex from Student where age<18";
Query query=session.createQuery(sql);
List list=query.list();
for (int i = 0; i < list.size(); i++) {
Object[] obj=(Object[])list.get(i);
System.out.println(obj[0]+" "+obj[1]);
}
util.HibernateSessionFactory.closeSession();
}
}
因为指定了查询的内容,所以返回在list里面已经不是属性而是对象了,通过获取对象,get到里面的属性值,就可以在控制台上打印信息了。
指定参数:
package TEST;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class Query3 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
int age=14;
String sql="select name,sex from Student where age<:age";
Query query=session.createQuery(sql);
query.setInteger("age",age);
List list=query.list();
for (int i = 0; i < list.size(); i++) {
Object[] obj=(Object[])list.get(i);
System.out.println(obj[0]+" "+obj[1]);
}
util.HibernateSessionFactory.closeSession();
}
}
不过jdk从1.9开始就不赞同使用Integer去构建包了。
批量查询——准则查询(Criteria )
Query4.java:
package TEST;
import PO.Student;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import java.util.List;
public class Query4 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
Criteria criteria=session.createCriteria(Student.class);
criteria.add(Restrictions.le("age",18));
List list=criteria.list();
for (int i = 0; i < list.size(); i++) {
Student student=(Student)list.get(i);
System.out.println(student.getName()+" "+student.getSex());
}
util.HibernateSessionFactory.closeSession();
}
}
不过Criteria方法已经过时,现在批量查询建议使用jpa做。
Criteria看上去比较麻烦,但是在一复合查询中,criteria有着无与伦比的优势。比如分页技术,可以利用criteria的setFirstResult和setMaxResult。
setFirstResult表示从第几条记录开始显示。
setMaxResult表示显示几条记录。
Query5.java:
package TEST;
import PO.Student;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import java.util.List;
public class Query4 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
Criteria criteria=session.createCriteria(Student.class);
criteria.add(Restrictions.le("age",18));
criteria.setFirstResult(1);//从第一条开始显示
criteria.setMaxResults(3);//显示3条记录
List list=criteria.list();
for (int i = 0; i < list.size(); i++) {
Student student=(Student)list.get(i);
System.out.println(student.getName()+" "+student.getSex());
}
util.HibernateSessionFactory.closeSession();
}
}
批量查询——SQL查询
如果使用SQL查询,就不怎么使用Hibernate了。
Query5.java:
package TEST;
import PO.Student;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import java.util.List;
public class Query5 {
public static void main(String[] args) {
Session session=util.HibernateSessionFactory.getSession();
SQLQuery sqlQuery=session.createSQLQuery("select * from user");
sqlQuery.addEntity(Student.class);
List list=sqlQuery.list();
for (int i = 0; i < list.size(); i++) {
Student student=(Student)list.get(i);
System.out.println(student.getAccount());
}
util.HibernateSessionFactory.closeSession();
}
}
如何在Hibernate里面调用存储过程
java.sql.Connection conn=session.connection();
conn.prepareCall(arg0);
小结
Hibernate的深入学习,有许多的API可供开发者调用,无需精通数据库操作,十分方便。