1.Hibernate的概述
1.1 Hibernate框架的作用
Hibernate框架是一个数据访问框架(也叫持久层框架,可将实体对象变成持久对象,详见第5章)。通过Hibernate框架可以对数据库进行增删改查操作,为业务层构建一个持久层。可以使用它替代以前的JDBC访问数据。
1.2 Hibernate访问数据库的优点
1) 简单,可以简化数据库操作代码。
2) Hibernate可以自动生成SQL,可以将ResultSet中的记录和实体类自动的映射(转化)。
3)Hibernate不和数据库关联,是一种通用的数据库框架(支持30多种数据库),可以方便数据库移植。任何数据库都可以执行它的API。因为Hibernate的API中是不涉及SQL语句的,它会根据Hibernate的配置文件,自动生成相应数据库的SQL语句
1.3 JDBC访问数据库的缺点
1)需要编写大量的复杂的SQL语句、表字段多时SQL也繁琐、设置各个问号值。
2)需要编写实体对象和记录之间的代码,较为繁琐。
3)数据库移植时需要修改大量的SQL语句。
1.4 Hibernate的设计思想
Hibernate是基于ORM(Object Relation Mapping)思想设计的,称为对象关系映射。负责Java对象和数据库表数据之间的映射。
Hibernate是一款主流的ORM工具,还有其他很多ORM工具,如:MyBatis(以前叫iBatis)、JPA。Hibernate功能比MyBatis强大些,属于全自动类型,MyBatis属于半自动。但全自动会有些不可控因素,因此有些公司会用MyBatis。
ORM工具在完成Java对象和数据库之间的映射后: HQL
1)在查询时,直接利用工具取出“对象”(不论是查询一条记录还是多条记录,取出的都是一个个对象,我们不用再去转化实体了)。
2)在增删改操作时,直接利用工具将“对象”更新到数据库表中(我们不用再去把对象转成数据了)。
3)中间的SQL+JDBC细节,都被封装在了工具底层,不需要程序员参与。
u 注意事项:
v Java程序想访问数据库,只能通过JDBC的方式,而Hibernate框架也就是基于ORM思想对JDBC的封装。
v Hibernate是以“对象”为单位进行数据库的操作。
2.Hibernate的基本使用
2.1 Hibernate的主要结构
1)hibernate.cfg.xml(仅1个):Hibernate的主配置文件,主要定义数据连接参数和框架设置参数。
u 注意事项:就是个xml文件,只是名字比较奇葩!
2)Entity实体类(n个,一个表一个):主要用于封装数据库数据。
3)hbm.xml映射文件(n个):主要描述实体类和数据表之间的映射信息。描述表与类,字段与属性的对应关系。
u 注意事项:hbm.xml是个后缀,如:命名可写Cost.hbm.xml。
2.2 Hibernate主要的API
1)Configuration:用于加载hibernate.cfg.xml配置信息。用于创建SessionFactory。
2)SessionFactory:存储了hbm.xml中描述的信息,内置了一些预编译的SQL,可以创建Session对象。
3)Session:负责对数据表执行增删改查操作。表示Java程序与数据库的一次连接会话,是对以前的Connection对象的封装。和JSP中的session不是一回事,就是名字一样而已。
4)Query:负责对数据表执行特殊查询操作+增删改。
5)Transaction:负责Hibernate操作的事务管理。默认情况下Hibernate事务关闭了自动提交功能,需要显式的追加事务管理(如调用Transaction对象中的commit();提交事务)!
u 注意事项:
v 这些API都是在Hibernate包下的,导包别导错!
v 第一次访问数据库比较慢,比较耗资源,因为加载的信息多。
2.3 Hibernate使用步骤
step1:建立数据库表。
step2:建立Java工程(Web工程也可),引入Hibernate开发包和数据库驱动包。必须引入的包:hibernate3.jar、cglib.jar、dom4j.jar、commons-collections.jar、commons-logging.jar…等,下载地址:http://hibernate.org/orm/downloads/
step3:添加hibernate.cfg.xml配置文件,文件内容如下:
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <!-- Generated by MyEclipse Hibernate Tools. --> <hibernate-configuration> <session-factory> <!-- 指定方言,决定Hibernate生成哪种SQL --> <property name="dialect"> org.hibernate.dialect.MySQLDialect </property> <property name="connection.url"> jdbc:mysql://localhost:3306/hibdata </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="myeclipse.connection.profile">mysql</property> <!-- 框架参数,将hibernate底层执行的SQL语句从控制台显示 --> <property name="show_sql">true</property> <!-- 格式化显示的SQL --> <property name="format_sql">true</property> <!-- 指定映射描述文件 --> <mapping resource="com/hxh/pojo/News.hbm.xml" /> </session-factory> </hibernate-configuration>
注意事项:应该放在源文件的src目录下,默认为hibernate.cfg.xml。文件内容是Hibernate工作时必须用到的基础信息。
step4:编写Entity实体类(也叫POJO类),例如:新闻实体类News
private Integer nid; private String title; private String content; private String photo; private Integer ntid;……getter/setter方法
注意事项:POJO类表示普通类(Plain Ordinary Old Object),没有格式的类,只有属性和对应的getter/setter方法,而没有任何业务逻辑方法的类。这种类最多再加入equals()、hashCode()、toString()等重写父类Object的方法。不承担任何实现业务逻辑的责任。
step5:编写hbm.xml映射(文件)描述信息:映射文件用于指明POJO类和表之间的映射关系(xx属性对应xx字段),一个类对应一个映射文件。例如:News.hbm.xml内容如下:
<?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"> <!-- 定义COST_CHANG表和Cost类型之间的映射信息 --> <hibernate-mapping><!-- <hibernate-mapping package="包名写这也行"> --> <!-- name:包名.类名,指定是哪个类;table:数据库中哪个表;catalog:对Oracle而言为哪个数据库,对MySQl而言为某个用户(MySQl是在用户下建表,Oracle是在库中建表), 不写也行(若用工具则会自动生成)。例如,select * from news_chang则会在hibernate.cfg配置文件中定义的库(或用户)下去找表。若写了则为select * from system.news_chang --> <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- 此处表示的是进行POJO类与数据表之间的映射 --> <hibernate-mapping> <!-- 配置的是类的相关定义,name表示类的名字,同时设置table对应的数据表,catalog表示的是数据库的名字 --> <class name="com.offcn.pojo.News" table="news" catalog="hibdata"> <!-- 配置主键,name表示的是类之中的属性名称,type表示的是News类的nid类型 --> <id name="nid" type="java.lang.Integer"> <!-- News类中的nid属性与数据表之中对应的列,列名称是nid --> <column name="nid" /> <!-- 主键的生成方式,本次使用的是数据表数据的自增长 --> <generator class="native"></generator> </id> <!-- 配置类之中的其它普通属性信息,name表示的是News类中的属性名称 --> <property name="title" type="java.lang.String"> <!-- 定义类中的属性与表之中的映射字段 --> <column name="title" length="50" not-null="true" /> </property> <property name="content" type="java.lang.String"> <column name="content" length="65535" /> </property> <property name="photo" type="java.lang.String"> <column name="photo" length="200" /> </property> <property name="ntid" type="java.lang.Integer"> <column name="ntid" /> </property> </class> </hibernate-mapping>
注意事项:
映射文件默认与POJO类放在一起;命名规则为:类名.hbm.xml。
hbm.xml中已写出的属性与字段的映射要一一对应,若表中没有某个字段,却写了映射关系,则报错:找不到实体类。
step6:利用Hibernate API实现DAO
1)新建HibernateUtil类,用于封装创建Session的方法。如下:每个用户会对应一个Session,但是SessionFactory是共享的。
public class HibernateUtil { private static SessionFactory sf; static{//不用每次都加载配置信息,所以放static块中,否则每次都加载会耗费资源 Configuration conf=new Configuration();//加载主配置hibernate.cfg.xml conf.configure("/hibernate.cfg.xml"); sf=conf.buildSessionFactory();//获取SessionFactory } public static Session getSession(){//获取Session Session session =sf.openSession(); return session; } }
2)新建NewsDao接口
package com.hxh.dao; import java.util.List; import com.hxh.pojo.News; public interface NewsDao { public News findById(int id); public void save(News news); public void delete(int id); public void update(News news); public List<News> findAll(); }
3)新建NewsDaoImpl类,用于实现NewsDao接口
package com.hxh.dao; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import com.hxh.pojo.News; import com.hxh.utils.HibernateUtil; public class NewsDaoImpl implements NewsDao { private Session session; public NewsDaoImpl(){ session=HibernateUtil.getSession(); } /* * @see com.offcn.dao.NewsDao#findById(int) * /** get方法执行查询,按主键当条件查询,如何判断是主键,是根据写的描述文件来定, * get方法就是findById,就是按主键去查,需指定:操作哪个类和id(主键)条件值即可,其他条件查询做不了 */ @Override public News findById(int id) { // TODO Auto-generated method stub News news=(News) session.get(News.class, id); session.close(); return news; } /** save方法执行增加操作,注意1:获取事务并开启,增删改要注意,查询可以不管事务,因为没对数据库进行修改;注意2:主键值根据hbm.xml中的<generator>定义生成,执行后,会先获取序列值,再去做insert操作。 即先:select COST_SEQ_CHANG.nextval from dual; 然后:insert into …… */ @Override public void save(News news) { // TODO Auto-generated method stub News vo = new News() ; vo.setTitle("今天是周五啦,高兴不?"); vo.setPhoto("nophoto.jpg"); vo.setContent("据说今天有中到大雨,小心被淹死哦,哈哈哈,可以免费游泳!"); // 6、打开事务 Transaction tran = session.beginTransaction() ; // 7、执行更新 session.save(vo); // 8、提交事务 tran.commit(); // 9、关闭连接 session.close() ; } /** delete方法执行删除操作,由于Hibernate以“对象”为单位进行数据库操作, * 所以这里要传进去一个对象,虽然是个对象,但还是按主键做条件删除,只要把主键值设置上就行,其他非主键值不用管。也可先通过id查再删 */ @Override public void delete(int id) { // TODO Auto-generated method stub Transaction tx=session.beginTransaction(); News news=new News(); news.setContent("ijikij即可立即"); session.delete(news); tx.commit(); session.close(); } /* * @see com.offcn.dao.NewsDao#update(com.offcn.pojo.News) */ @Override public void update(News news) { // TODO Auto-generated method stub session=HibernateUtil.getSession(); Transaction tx=session.beginTransaction(); session.update(news);//将cost对象更新到数据库 tx.commit(); session.close(); } /* * ** 特殊查询,SQL语句:String sql="select * from COST_CHANG"; HQL语句:String hql="from Cost"; (Hibernate Query Language)是面向对象的查询语句。 from后写映射的类名,它是Hibernate中特有的查询语句,根据映射的类去查询。 */ @Override public List<News> findAll() { session=HibernateUtil.getSession(); String hql="from News";//HQL语句 Query query=session.createQuery(hql); List<News> list=query.list();//执行查询,返回List集合 session.close(); return list; } }
4)新建TestCostDAO类,使用junit测试
package hxh.offcn.test; import java.sql.Date; import java.util.List; import org.junit.Test; import com.hxh.dao.NewsDao; import com.hxh.dao.NewsDaoImpl; import com.hxh.pojo.News; public class TestDao { @Test public void testFindById(){//当get方法没有记录时,返回null NewsDao NewsDao = new NewsDaoImpl(); News News = NewsDao.findById(1); System.out.println(News); } @Test public void testSave(){//id主键列由Hibernate管理,这里不用设置 News vo = new News() ; vo.setTitle("w今天是周五啦,高兴不?"); vo.setPhoto("nophoto.jpg"); vo.setContent("据说今天有中到大雨,小心被淹死哦,哈哈哈,可以免费游泳!"); NewsDao NewsDao = new NewsDaoImpl(); NewsDao.save(vo); } @Test public void testUpdate(){ NewsDao NewsDAO=new NewsDaoImpl(); /** 注意事项:更新部分字段,不能和实现类中的删除那样,做一个对象出来!否则没设置的字段将被改为空! 即不能:News News=new News(); News.setId(90); News.setStatus("1"); News.setStartTime(new Date(System.currentTimeMillis())); */ News news=NewsDAO.findById(1);//只能先通过id找到带有所有值的对象 news.setContent("我跟发新股够了"); NewsDAO.update(news); } @Test public void testDelete(){ NewsDao NewsDAO=new NewsDaoImpl(); NewsDAO.delete(1); } @Test public void testFindAll(){ NewsDao NewsDAO=new NewsDaoImpl(); List<News> list=NewsDAO.findAll(); for(News c:list){ System.out.println(c); } } }
3.关联映射
3.1一对多关系one-to-many
step1:新建项目,导入Hibernate开发包,借用学生班级实体:Student和Grade,配置hibernate.cfg.xml和两个实体的hbm.xml映射文件。
step2:一个年级对应多个学生,所以为一对多关系。因此为One方Grade实体类添加Set集合属性,以及对应的get/set方法。
//追加属性,用于存储相关联的学生信息 private Set<Student> students = new HashSet<Student>();
step3:在One方Grade.hbm.xml映射文件中,加入Set节点的映射
简单说明:
<set name="属性名"> <!-- 关联条件,column写外键字段,会默认的与其表的主键相关联 --> <key column="指定关联条件的外键字段"></key> <!-- 指定采用一对多关系,class指定关联的类型 --> <one-to-many class="要关联的另一方(N方)"/> </set>
具体实现:
<!-- 描述services属性,采用一对多关系加载service记录 --> <!-- 是list集合用<list name=""></list> set集合用<set name=""></set> --> <set name="students" cascade="all" lazy="false"> <key column="gid"/> <one-to-many class="com.hxh.pojo.Student"/> </set>
step4:借用5.17节4)中的step1中的HibernateUtil类
step5:新建TestOneToMany.java类用于测试
/** * 功能1:查询一个班级的所有学生信息 */ @Test public void findById(){ Session session = HibernateUtil.getSessionFactory().openSession(); Grade g = (Grade)session.get(Grade.class, 1); System.out.println(g.getGid()+" "+ g.getGname()+" "+ g.getGdesc()); Set <Student>students =g.getStudents(); for(Student stu:students){ System.out.println(stu.getSid()+" "+stu.getSname()+" "+stu.getSex()); } session.close(); } /** * 功能2:一名新生加入到一个现有的班中 */ @Test public void update() { Student stu = new Student(); stu.setSid(3); stu.setSname("李婷3"); stu.setSex("女"); Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tr = session.beginTransaction(); Grade g = (Grade)session.get(Grade.class, 1); /*Set studentset=g.getStudents(); System.out.println(studentset);*/ g.getStudents().add(stu); session.save(g); session.save(stu); tr.commit(); session.close(); } /** * 功能3:删除现有班级中一名学生 */ @Test public void deleteStudent(){ Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tr = session.beginTransaction(); Grade g = (Grade)session.get(Grade.class, 1); Student stu = (Student)session.get(Student.class, 1); g.getStudents().remove(stu); session.update(g); session.update(stu); tr.commit(); session.close(); } /** * 功能4:查询一个学生的班级信息 */ @Test public void findById2(){ Session session = HibernateUtil.getSessionFactory().openSession(); Student stu = (Student)session.load(Student.class, 1); System.out.println(stu.getSid() + " " + stu.getSname()); Grade grade = stu.getGrade(); System.out.println(grade.getGname()); System.out.println(grade.getGdesc()); session.close(); } /** * 功能5:一名现有学生转入到另一个现有班级中 */ @Test public void update2() { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tr = session.beginTransaction(); Grade g = (Grade)session.get(Grade.class, 2); Student stu = (Student)session.get(Student.class, 3); g.getStudents().add(stu); session.update(g); //session.update(stu); tr.commit(); session.close(); } }
6.2一对一关系one-to-one
step1:新建项目,导入Hibernate开发包,借用学生和考卷两个实体:Student和Paper,配置hibernate.cfg.xml和两个实体的hbm.xml映射文件。
step2:可一个学生对应一份考卷,所以为一对一关系。因此为Student实体类添加Paper属性,以及对应的get/set方法,同时为Paper实体类添加Student属性,以及对应的get/set方法。
//追加属性,用于存储关联的Paper信息
private Paper paper;//
Paper实体类
package com.hxh.pojo; public class Paper { private int pid; private String pdesc; private Student student; public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } public String getPdesc() { return pdesc; } public void setPdesc(String pdesc) { this.pdesc = pdesc; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } }
step3:配置Student.hbm.xml映射文件和Paper.hbm.xml
简单说明:
<one-to-one name="属性名" class="要关联的另一方类型Account" column="关联条件的外键字段"/> <!-- 指明外键字段,不写主键 -->
注意事项:此时没有<set name="属性名"></set>标签。
具体实现:
<one-to-one name="paper" class="com.hxh.pojo.Paper" lazy="false" cascade="all"/>
<?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"> <hibernate-mapping> <class name="com.hxh.pojo.Paper" table="PAPER" > <id name="pid" type="java.lang.Integer"> <column name="PID" /> <generator class="assigned" /> </id> <property name="pdesc" type="java.lang.String"> <column name="PDESC" length="50" not-null="true" /> </property> <many-to-one name="student" class="com.hxh.pojo.Student" unique="true" > <column name="sid"/> </many-to-one> </class> </hibernate-mapping>
step4:借用5.17节4)中的step1中的HibernateUtil类
step5:新建TestOne2one .java类用于测试
package com.hxh.test; import org.hibernate.Session; import org.hibernate.Transaction; import com.hxh.pojo.Paper; import com.hxh.pojo.Student; import com.hxh.utils.HibernateUtil; public class TestOne2one { public static void main(String[] args) { //save(); findById2(); } /** * 功能1:新增一个学生及其学生证信息 */ public static void save() { Student stu = new Student(); stu.setSid(11070130); stu.setSname("赵飞"); stu.setSex("男"); Paper paper = new Paper(); paper.setPid(123456); paper.setPdesc("北京大学学生证"); paper.setStudent(stu); stu.setPaper(paper); Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tr = session.beginTransaction(); session.save(stu); tr.commit(); session.close(); } /** * 功能2:根据学生证查询相应学生信息 */ public static void findById2() { Session session = HibernateUtil.getSessionFactory().openSession(); Paper paper= (Paper)session.get(Paper.class, 123456); System.out.println(paper.getPdesc()); System.out.println(paper.getStudent().getSname()); session.close(); } }