Hibernate初探之单表映射

来源:互联网 发布:mac截图怎么保存 编辑:程序博客网 时间:2024/05/24 07:13

第1章 Hibernate初始

什么是ORM

ORM(Object/RelationShip Mapping):对象/关系映射。利用面向对象思想编写的数据库应用程序最终是把对象的信息保存在关系型数据库中,于是要编写很多与底层数据库相关的Sql语句,而且:不同的数据库使用的SQL语法不同,比如PL/SQL与T/SQL。

Hibernate是一个开放源代码的对象关系映射框架它对JDBC进行了非常轻量级的对象封装。

使用Hibernate需要有以下4步:

  1. 创建Hibernate的配置文件hibernate.cfg.xml
  2. 创建持久化类
  3. 创建对象-关系映射文件
  4. 通过Hibernate API编写访问数据库的代码

导入Hibernate必须的jar包:

  • hibernate-release-4.2.4.Final\lib\required
  • 导入MySQL的jdbc驱动,MySQL-connector-Java-5.1.7-bin.jar
  • 导入Junit4的jar包(做单元测试用的),junit-4.10.jar

Hibernate Tools for Eclipse Plugins安装失败。低版本的下载不下来。高版本的不会用。很气。手打也是可以的。

编写第一个Hibernate例子

1. 创建hibernate的配置文件hibernate.cfg.xml

新建一个java project,选择src,右键 new file hibernate.cfg.xml。其中包含数据库连接信息。

<?xml version='1.0' encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC        "-//Hibernate/Hibernate Configuration DTD//EN"        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><hibernate-configuration>    <session-factory>        <property name="connection.username">root</property>        <property name="connection.password">asdf1234</property>        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>        <property name="connection.url">jdbc:mysql:///hibernate?useUnicode=true&amp;characterEncoding=UTF-8</property>        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>        <property name="show_sql">true</property>        <property name="format_sql">true</property>        <property name="hbm2ddl.auto">create</property>        <mapping resource="Students.hbm.xml"></mapping>    </session-factory></hibernate-configuration>

2. 创建持久化类

持久化类要遵循JavaBean的规范:

  1. JavaBean 必须申明为 public class 即:必须是公有的类
  2. JavaBean 的所有属性必须申明为 private 即:属性必须私有
  3. 通过 setter 方法和 getter 方法设值和取值
  4. 必须有一个公有无参构造方法

创建Students.java实体类

import java.util.Date;/** * 学生类 */public class Students {    private int sid; //学号    private String sname; //姓名    private String gender; //性别    private Date birthday; // 出生日期    private String address; // 地址    public Students() {    }    public Students(int sid, String sname, String gender, Date birthday, String address) {        this.sid = sid;        this.sname = sname;        this.gender = gender;        this.birthday = birthday;        this.address = address;    }    public int getSid() {        return sid;    }    public void setSid(int sid) {        this.sid = sid;    }    public String getSname() {        return sname;    }    public void setSname(String sname) {        this.sname = sname;    }    public String getGender() {        return gender;    }    public void setGender(String gender) {        this.gender = gender;    }    public Date getBirthday() {        return birthday;    }    public void setBirthday(Date birthday) {        this.birthday = birthday;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    @Override    public String toString() {        return "Students{" +                "sid=" + sid +                ", sname='" + sname + '\'' +                ", gender='" + gender + '\'' +                ", birthday=" + birthday +                ", address='" + address + '\'' +                '}';    }}

3. 创建关系映射文件和数据库

src new file Students.hbm.xml,把持久化类映射为名为STUDENTS的数据库表,并使类中的属性与数据库表的字段一一对应

<?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>    <class name="Students" table="STUDENTS">        <id name="sid" type="int">            <column name="SID"></column>            <generator class="assigned"></generator>        </id>        <property name="sname" type="java.lang.String">            <column name="SNAME"></column>        </property>        <property name="gender"  type="java.lang.String">            <column name="GENDER"></column>        </property>        <property name="birthday" type="java.util.Date">            <column name="BIRTHDAY"></column>        </property>        <property name="address" type="java.lang.String">            <column name="ADDRESS"></column>        </property>    </class></hibernate-mapping>

注意:创建好映射文件Students.hbm.xml后,需要加到hibernate.cfg.xml文件中。程序执行时,会读取hibernate.cfg.xml文件。只有在其中添加了的映射文件,才会被处理。
在sessionfactory标签的最后添加 mapping映射。

<session-factory>    ...    ...    <mapping resource="Students.hbm.xml"></mapping></session-factory>

打开navicat 创建名为hibernate数据库。字符集和排序规则要使用utf8。

4. 通过Hibernate API编写访问数据库代码

在这里我们选择在测试类中测试我们的第一个Hibernate小程序。

先需要认识几个Junit的注释:

  • @Test:测试方法
  • @Before:初始化方法
  • @After:释放资源

执行顺序:@Before->@Test->@After

建立测试文件夹。选中项目右键new->Source Folder 输入文件夹的名称test。
创建名为StudentsTest的类
在@Before的方法中构建使用Hibernate的初始化信息
在@Test的方法中执行把一条student的信息保存到数据库的操作
在@After的方法中提交事务并释放资源。

import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;import org.hibernate.service.ServiceRegistry;import org.hibernate.service.ServiceRegistryBuilder;import org.junit.After;import org.junit.Before;import org.junit.Test;import java.util.Date;/** * 测试类 */public class StudentsTest {    private SessionFactory sessionFactory;    private Session session;    private Transaction transaction;//事务对象    @Before    public void init() {        //创建配置对象        Configuration config = new Configuration().configure();        //创建服务注册对象        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();        //创建会话工厂        sessionFactory = config.buildSessionFactory(serviceRegistry);        //创建会话对象        session = sessionFactory.openSession();        //开启事务        transaction = session.beginTransaction();    }    @After    public void destory() {        transaction.commit(); //提交事务        session.close(); //关闭会话        sessionFactory.close(); //关闭会话工厂    }    @Test    public void testSaveStudents() {        Students s = new Students(1,"小雪","女",new Date(),"上海");        session.save(s); //保存对象进入数据库    }}

选中testSaveStudents()方法,右键run as->Junit Test
在控制台打印的信息:

Hibernate:     drop table if exists STUDENTSHibernate:     create table STUDENTS (        SID integer not null,        SNAME varchar(255),        GENDER varchar(255),        BIRTHDAY datetime,        ADDRESS varchar(255),        primary key (SID)    )八月 21, 2017 2:05:25 下午 org.hibernate.tool.hbm2ddl.SchemaExport executeINFO: HHH000230: Schema export completeHibernate:     insert     into        STUDENTS        (SNAME, GENDER, BIRTHDAY, ADDRESS, SID)     values        (?, ?, ?, ?, ?)

第2章 Hibernate进阶

hibernate.cfg.xml常用配置

属性名字 含义 hibernate.show_sql 是否把Hibernate运行时的SQL语句输出到控制台,编码阶段便于测试。 hibernate.formate_sql 输出到控制台的SQL语句是否要进行排版,便于阅读。建议设置为true。 hbm2ddl.auto 可以帮助由java代码生成数据库脚本,进而生成具体的表结构。create(每次生成新的表结构,原来的表存在会先删除原来的表);update(在原有的表的基础之上进行更新);create-drop(先创建,再删除);validate(对原有的表结构进行验证) hibernate.default_schema 会给所有的表名前面加上前缀。如hibernate.STUDENTS hibernate.dialect 配置Hibernate数据库方言,Hibernate可针对特定的数据库进行优化

session简介

hibernate的执行流程

  1. 创建配置对象Configuration,配置对象的作用就是读取配置文档Hibernate.cfg.xml
  2. 获得Configuration的目的是创建SessionFactory对象。创建SessionFactory时就会读取相应的里面所加载的对象关系映射文件。
  3. 获得了SessionFactory对象后就可创建Session对象。Session对象类似于JDBC中的Connection。获得了Session对象,就表示获得了数据库连接对象。就可以执行Session中相应的方法,如:save、delecte、update、get。
  4. 在执行Session的方法时,必须要开启事务。这些方法都要封装在事务当中。
  5. 执行完方法后,要先提交事务,再关闭session。

session简介

hibernate是对jdbc的封装,不建议直接使用jdbc的connection操作数据库,而是通过使用session操作数据库。

所以,session可以理解为操作数据库的对象。我们要使用Hibernate操作数据库之前,就必须获得一个session的实例。

session与connection,是多对一关系,每个session都有一个与之对应的connection,一个connection不同时刻可以提供多个session使用。

把对象保存在关系数据库中需要调用session的各种方法,如:save(),update(),delete(),createQuery()等。

transaction简介

hibernate对数据的操作都是封装在事务当中,并且默认是非自动提交的方式。所以用session保存对象时,如果不开启事务,并且手工提交事务,对象并不会真正保存在数据库中。

如果想让hibernate像jdbc那样自动提交事务,必须调用session对象的doWork()方法,获得jdbc的connection后,设置其为自动提交事务模式,但通常并不推荐这样做。

    @Test    public void testSaveStudents() {        Students s = new Students(2,"小小雪","女",new Date(),"上海");        session.doWork(new Work() {            @Override            public void execute(Connection connection) throws SQLException {                connection.setAutoCommit(true);            }        });        session.save(s); //保存对象进入数据库        session.flush();    }

session详解

如何获取session对象?

  1. openSession
  2. getCurrentSession

如果使用getCurrentSession需要在hibernate.cfg.xml文件中进行配置:
如果是本地事务(jdbc事务)
<property name="hibernate.current_session_context_class">thread</property>
如何是全局事务(jta事务)
<property name="hibernate.current_session_context_class">jta</property>
全局事务和本地事务详解

openSession与getCurrentSession的区别

  1. getCurrentSession在事务提交或者回滚之后会自动关闭,而openSession需要手动关闭。如果使用openSession而没有手动关闭,多次提交之后会导致连接池溢出。
  2. openSession每次创建新的session对象,getCurrentSession使用现有的session对象。

hbm配置文档

hibernate-mapping标签的属性

  • schema=”schemaName”,模式的名字
  • catalog=”catalogName”,目录的名称
  • default-cascade=”cascade_style”,级联风格
  • default-access=”field|property|ClassName”,访问策略
  • default-lazy=”true|false”,加载策略
  • package=”packagename”,默认的包名

class标签的属性

  • name,表示对象关系映射映射的类
  • table,映射成数据库的表名
  • batch-size,一次可以抓取多少条记录
  • where,条件
  • entity-name,同一个类支持映射成多个表

id标签,表示表的主键,它的属性

  • name,要映射的属性
  • type,数据类型
  • column,映射成数据库中表的字段的名称
  • length,长度
  • <generator class="generatorClass"/>,主键生成策略

主键生成策略

标识符生成器 描述 increment 适用于代理主键。由hibernate自动以递增方式生成 indentity 适用于代理主键。由底层数据库生成标识符。 sequence 适用于代理主键。hibernate根据底层数据库的序列生成标识符,这要求底层数据库支持序列。 hilo 适用于代理主键。hibernate分局high/low算法生成标识符。 seqhilo 适用于代理主键。使用一个高/低位算法来高效的生成long,short或者int类型的标识符。 native 适用于代理主键。根据底层数据库对自动生成标识符的方式,自动选择identity、sequence或hilo。 uuid.hex 适用于代理主键。hibernate采用128位的UUID算法生成标识符。 uuid.string 适用于代理主键。UUID被编码成一个16字符长度的字符串。 assigned 适用于自然主键。由Java应用程序负责生成标识符。 foreign 适用于代理主键。使用另一个相关联的对象的标识符。

Hibernate单表操作

单一主键

单一主键是指表中由某一列充当主键。
两种常见的生成但已逐渐策略:

  1. assigned 由java应用程序负责生成(手工赋值)。
  2. native 由底层数据库自动生成标识符,如果是MySQL就是increment,如果是Oracle就是sequence。

hibernate基本类型

Hibernate映射类型 Java类型 Integer/int java.lang.Integer/int long java.lang.Long/long short java.lang.Short/short byte java.lang.Byte/byte float java.lang.Float/float double java.lang.Double/double big_decimal java.math.BigDecimal character java.lang.Character/java.lang.String/char string java.lang.String boolean/yes_no/true_false java.lang.Boolean/Boolean date java.util.Date/java.sql.Date timestamp java.util.Date/java.util.Timestamp calendar java.util.Calendar calendar_date java.util.Calendar

日期类型的详细映射关系

映射类型 Java类型 标准SQL类型 描述 date java.util.Date/java.sql.Date DATE 代表日期:yyyy-MM-dd time java.util.Date/java.sql.Time TIME 代表时间:hh:mi:ss timestamp java.util.Date/java.sql.Timestamp TIMESTAMP 代表时间和日期:yyyymmddhhmiss calendar java.util.Calendar TIMESTAMP 代表时间和日期:yyyymmddhhmiss calendar_date java.util.Calendar DATE 代表日期:yyyy-MM-dd

前面例子中数据库中BIRTHDAY字段是带时分秒的,如果想只显示为日期,则要在Student.hbm.xml文件中把birthday属性的type该为Hibernate映射类型中的date就可以了

 <property name="birthday" type="date">     <column name="BIRTHDAY"></column></property>

对象类型

映射类型 Java类型 标准SQL类型 MYSQL类型 Oracle类型 binary byte[] VARCHAR(或BLOB) BLOB BLOB text java.lang.String CLOB TEXT CLOB clob java.sql.Clob CLOB TEXT CLOB blob java.sql.Blob BLOB BLOB BLOB

text、clob对应大数据文本类型
blob对应二进制数据类型,音频、视频、图片。
MySQL中不支持SQL的CLOB类型,在MySQL中,用 TEXT,
MEDIUMTEXT以及LONGTEXT类型来表示长度超过255的长文本数据。

使用Hibernate写入对象类型的数据:

    //使用hibernate写入图片数据    @Test    public void testWriteBlob() throws Exception {        Students s = new Students(1,"小雪","女",new Date(),"上海");        File file = new File("f:"+File.separator+"girl.jpg");        InputStream input = new FileInputStream(file);        Blob image = Hibernate.getLobCreator(session).createBlob(input,input.available());        s.setPicture(image);        session.save(s); //保存对象进入数据库    }    //读取数据库中存的图片,并保存为新的图片    @Test    public void testReadBlob() throws Exception {        Students s = (Students) session.get(Students.class, 1);        //获得blob对象        Blob image = s.getPicture();        //获得照片的输入流        InputStream input = image.getBinaryStream();        //创建输出流        File file = new File("f:"+File.separator+"football.jpg");        //获得输出流        OutputStream output = new FileOutputStream(file);        //创建缓冲区        byte[] buff = new byte[input.available()];        //把图片读到缓冲区        input.read(buff);        output.write(buff);        input.close();        output.close();    }

这两个测试方法中遇到了两个问题:

  1. 测试testWriteBlob()方法时,插入数据失败,显示:ERROR: HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in Students entry (don't flush the Session after an exception occurs) 解决方案:在Students.hbm.xml中主键生成策略使用了native,换成默认的assign就好了<generator class="assigned"></generator>
  2. 解决了上面问题后,再次执行测试方法,测试未通过。显示:
    java.lang.AbstractMethodError: Method com/mysql/jdbc/PreparedStatement.setBinaryStream(ILjava/io/InputStream;J)V is abstract
    解决方案:是由于数据库驱动版本过低造成,之前我用的驱动为:mysql-connector-java-5.0.8.jar,改成mysql-connector-Java-5.1.39.jar就可以了。
    PS:今天发现一个下载驱动JAR包的好方法。在找mysqljdbc驱动时,实在是遇到了很多问题。最开始的时候,只会百度,下载了一堆,然后也可能不好用;后来学会了maven,通过在maven项目中添加依赖,然后到本地仓库中找,这样想要什么版本的JAR包就可以有什么版本的JAR包;但今天,我想要5.1.39版本的JDBC驱动,写好依赖maven install失败了。刚好给了我阿里云上JAR包仓库的地址。这里真滴是啥都有。如5.1.39版本的驱动。

组件属性

实体类中的某个属性属于用户自定义的类的对象。

<component name="address" class="Address">     <property name="postcode"  column="POSTCODE">     <property name="phone"  column="PHONE">     <property name="address"  column="ADDRESS"></component>

在我们的Students类中有address属性,它是属于Address类的对象。而Address类又有三个字段。那么久把address这个属性称为组件属性。
创建Address类

public class Address {    private String postcode;    private String phone;    private String address;    public Address() {    }    public Address(String postcode, String phone, String address) {        super();        this.postcode = postcode;        this.phone = phone;        this.address = address;    }    public String getPostcode() {        return postcode;    }    public void setPostcode(String postcode) {        this.postcode = postcode;    }    public String getPhone() {        return phone;    }    public void setPhone(String phone) {        this.phone = phone;    }    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }}

修改Students类,把原来的String address 换为 Address类型

import java.sql.Blob;import java.util.Date;/** * 学生类 */public class Students {    private int sid; //学号    private String sname; //姓名    private String gender; //性别    private Date birthday; // 出生日期    private Blob picture;    private Address address; // 地址    public Students() {    }    public Students(int sid, String sname, String gender, Date birthday) {        this.sid = sid;        this.sname = sname;        this.gender = gender;        this.birthday = birthday;//        this.address = address;    }    public Students(String sname, String gender, Date birthday) {        this.sname = sname;        this.gender = gender;        this.birthday = birthday;//        this.address = address;    }    public int getSid() {        return sid;    }    public void setSid(int sid) {        this.sid = sid;    }    public String getSname() {        return sname;    }    public void setSname(String sname) {        this.sname = sname;    }    public String getGender() {        return gender;    }    public void setGender(String gender) {        this.gender = gender;    }    public Date getBirthday() {        return birthday;    }    public void setBirthday(Date birthday) {        this.birthday = birthday;    }    public Blob getPicture() {        return picture;    }    public void setPicture(Blob picture) {        this.picture = picture;    }    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }    @Override    public String toString() {        return "Students{" +                "sid=" + sid +                ", sname='" + sname + '\'' +                ", gender='" + gender + '\'' +                ", birthday=" + birthday +                ", address='" + address + '\'' +                '}';    }}

修改Students.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>    <class name="Students" table="STUDENTS">        <id name="sid" type="int">            <column name="SID"></column>            <generator class="assigned"></generator>        </id>        <property name="sname" type="java.lang.String">            <column name="SNAME"></column>        </property>        <property name="gender"  type="java.lang.String">            <column name="GENDER"></column>        </property>        <property name="birthday" type="date">            <column name="BIRTHDAY"></column>        </property>        <!-- <property name="address" type="java.lang.String">            <column name="ADDRESS"></column>        </property> -->        <property name="picture" type="java.sql.Blob">            <column name="PICTURE"></column>        </property>        <component name="address" class="Address">             <property name="postcode"  column="POSTCODE" />             <property name="phone"  column="PHONE" />             <property name="address"  column="ADDRESS" />        </component>    </class></hibernate-mapping>

hibernate.cfg.xml中<property name="hbm2ddl.auto">create</property>

测试案例的方法:

    @Test    public void testSaveStudents() {        Students s = new Students("小小雪","女",new Date());//当Student.hbm.xml中主键生成策略为native是,手动赋值id是不起作用的。此时数据库中id值为1、        Address address = new Address("154600", "132131", "七台河市");        s.setAddress(address);        session.save(s); //保存对象进入数据库    }

单表CRUD操作实例

  • save
  • update
  • delete
  • get/load (查询单个记录)
    @Test    public void testGetStudents() {        Students s = (Students) session.get(Students.class, 1);        System.out.println(s);    }    @Test    public void testLoadStudents() {        Students s = (Students) session.load(Students.class, 1);        System.out.println(s);    }    @Test    public void testUpdateStudents() {        Students s = (Students) session.get(Students.class, 1);        s.setSname("星星");        s.setGender("男");        session.update(s);    }    @Test    public void testDeleteStudents() {        Students s = (Students) session.get(Students.class, 1);        session.delete(s);    }

get与load的区别

  1. 在不考虑缓存的情况下,get方法会在调用之后立即向数据库发出sql语句,返回持久化对象。load方法会在调用后返回一个代理对象。该代理对象只保存了实体对象的id,直到使用对象的非主键属性时才会发出sql语句。
  2. 查询数据库中不存在数据时,get方法返回null,load方法抛出异常org.hibernate.ObjectNotFoundException
    @Test    public void testGetStudents() {        Students s = (Students) session.get(Students.class, 1);//      System.out.println(s);//注释掉该句,控制台仍然输出select语句        System.out.println(s.getClass().getName());//利用反射,输出对象对应的类的名称  结果:Students    }    @Test    public void testLoadStudents() {        Students s = (Students) session.load(Students.class, 1);//      System.out.println(s);//注释掉该句,控制台不输出select语句        System.out.println(s.getClass().getName());//利用反射,输出对象对应的类的名称  结果:Students_$$_javassist_0    }
原创粉丝点击