重拾Hibernate框架——实体类、主键生成策略、session绑定本地线程

本文详细介绍了Hibernate框架的使用方法,包括实体类编写规则、主键生成策略、CRUD操作、事务处理等内容,并通过具体案例展示了如何进行实体类的操作。

目录

实体类(持久化类)的编写规则

  1. 属性私有化,即private修饰;
  2. 属性提供getter和setter方法;
  3. 要求实体类提供一个属性,并作为唯一值(一般使用ID值);
  4. 实体类属性建议使用基本数据类型对应的包装类,而不使用基本数据类型。

为什么“实体类属性建议使用基本数据类型对应的包装类,而不使用基本数据类型。”?
  假设存在这么一个实体类Student类,具有int score属性,那么:
当学生分数为0时,score = 0;当学生没有参加考试时,int类型的score属性就无法表示这个含义了。
但是如果score为Integer类型,则可以以 score = null 进行表示。


Hibernate主键生成策略

  实体类被要求提供一个属性作为唯一值,该属性就对应数据库表中的主键。在Hibernate中主键由配置好的策略进行生成。
(主键生成策略在对应的实体类映射配置文件中进行设置)
如:

<!--
    id标签
        name属性:实体类中作为唯一标识的属性的名称
        column属性:生成的表字段名称
-->
<id name="uid" column="uid">
    <!--
        设置数据库表中ID的增长策略
        native:生成表的id是主键自动增长的。
    -->
    <generator class="native"/>
</id>

主键生成策略有:(列举出几个常见的)
1. increment:用于long、short或int类型,由Hibernate自动以递增的方式生成唯一标识符,每次增量为1。只有当没有其他进程向同一张表中插入数据时才可以使用,不能在集群环境下使用。适用于代理主键。
2. identity:采用底层数据库本身提供的主键生成标识符,条件是数据库支持自动增长数据类型。在MySQL数据库中可以使用该生成器,该生成器要求在数据库中把主键定义成自增长类型。适用于代理主键。Oracle数据库并不适用。
3. sequence:Hibernate根据底层数据库序列生成标识符。条件是数据库之间序列。适用于代理主键。在Oracle数据库中可以使用该生成器。
4. native:根据底层数据库对自动生成表示符的能力来选择identity、sequence、hilo三种生成器中的一种,适合跨数据库平台开发。适用于代理主键。
5. uuid:使用128位的UUID算法计算一个唯一的值,会使用IP地址及相关的计算机硬件信息。计算结果位32位的16进制数,对应的主键类型必须为String。
注:
  increment和identity的区别:这两种主键生成方式都是提供自增长策略,但是原理并不一样。increment的主键自增长由Hibernate维护,而identity的主键自增长由底层数据库维护。如果采用MySQL的自增长(这里假设使用MySQL数据库,且主键生成策略设置为identity),插入数据时Hibernate生成的SQL语句中将不包含id主键列数据。该主键的当前值、下一个值由数据库自己维护。如果使用Hibernate的自增长(即设置了increment主键生成策略),插入数据时Hibernate生成的SQL语句将包含id主键列,并由Hibernate维护该主键的当前值以及下一个值。
  increment,自增长类型,由Hibernate而不是数据库维护,因此即使Oracle等不支持自增长类型的数据库也可以使用。

实体类操作

案例准备

(以下说明均基于该案例进行说明)
实体类 User.java

package com.wm103.entity;
import java.io.Serializable;

public class User implements Serializable {
    /*Hibernate要求实体类有一个属性是唯一的。*/
    private int uid;
    private String username;
    private String password;
    private String address;

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "uid=" + uid +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

实体类的映射文件 User.hbm.xml

<?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">
<hibernate-mapping>
    <class name="com.wm103.entity.User" table="t_user">
        <id name="uid" column="uid">
            <generator class="native"/>
        </id>

        <property name="username" column="username"></property>
        <property name="password" column="password"></property>
        <property name="address" column="address"></property>
    </class>
</hibernate-mapping>

核心配置文件 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"></property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql:///hibernate_day01?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">update</property>

        <mapping resource="com/wm103/entity/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

Hibernate工具类 HibernateUtil.java

package com.wm103.utils;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * Created by DreamBoy on 2017/5/19.
 */
public class HibernateUtil {
    // 单态模式的SessionFactory
    private static final SessionFactory sessionFactory;

    static {
        // 采用默认的hibernate.cfg.xml来启动一个Configuration的实例
        Configuration cfg = new Configuration().configure();
        sessionFactory = cfg.buildSessionFactory();
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void main(String[] args) {
        System.out.println(getSessionFactory());
    }
}

测试类 HibernateDemo.java

package com.wm103.hibernatetest;

import com.wm103.entity.User;
import com.wm103.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

/**
 * Created by DreamBoy on 2017/5/19.
 */
public class HibernateDemo {
    public void test() {
        // 1. 调用工具类得到sessionFactory
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();

        // 2. 获取session
        Session session = sessionFactory.openSession();

        // 3. 开启事务
        Transaction tx = session.beginTransaction();

        // 4. 具体逻辑
        // 后续提及的案例代码放在这里!!!

        // 5. 提交事务
        tx.commit();

        // 6. 关闭资源
        session.close();
        sessionFactory.close();
    }
}

对实体类CRUD操作

添加操作

  调用session里面的save方法,如:

User user = new User();
user.setUsername("梦小白");
user.setPassword("123");
user.setAddress("China");
session.save(user);

查询操作

  调用session里面的get方法,如:

User user = session.get(User.class, 2);
System.out.println(user);

修改操作

  调用session里面的update方法,如:

User user = session.get(User.class, 1);
user.setUsername("DreamBoy");
session.update(user);

删除操作

第一种 根据ID查询到的对象

User user = session.get(User.class, 2);
session.delete(user);

第二种 创建新对象设置主键ID值

User user = new User();
user.setUid(3);
session.delete(user);

saveOrUpdate方法

  既能实现添加操作,又能实现修改操作。如:
实现添加操作

User user = new User();
user.setUsername("User007");
user.setPassword("007");
user.setAddress("地下城");

// 实体类对象状态是瞬时态时,saveOrUpdate执行的是添加操作
session.saveOrUpdate(user);

实现修改操作

User user = session.get(User.class, 1);
user.setUsername("DreamBoy");

// 实体类对象状态是持久态时,saveOrUpdate执行的是修改操作
session.saveOrUpdate(user);

实体类对象状态

  实体类对象有三种状态:
(1)瞬时态:对象里面没有ID值,对象与session没有关联。如:

User user = new User();
user.setUserName("haha");
user.setPassword("1234");
user.setAddress("China");
session.save(user);

  在这里添加user记录以前,user对象既没有ID值也没有与session关联,此时user对象处于瞬时态。
(2)持久态:对象里面有ID值,且与session有关联。如:

User user = session.get(User.class, 1);

(3)托管态:对象里面有ID值,但是与session没有关系。

User user = new User();
user.setUid(3);
session.delete(user);

  在这里执行delete方法之前user对象处于托管态。

Hibernate一级缓存

(1)Hibernate一级缓存的特点:

  • Hibernate的一级缓存是默认打开的
  • Hibernate的一级缓存使用范围,是session范围,即从session创建到session关闭
  • Hibernate的一级缓存中,存储数据必须是持久态数据

(2)特性:持久态下修改实体类对象属性会自动更新数据库,如:

User user = session.get(User.class, 1);
user.setUsername("DreamBoy");
// session.update(user); // 这里不调用,同样在提交事务的时候,也会进行更新操作

Hibernate事务操作

规范写法

try {
    // 开启事务
    // ...
    // 提交事务
} catch() {
    // 回滚事务
} finally {
    // 关闭资源
}

案例

@Test
public void testTx() {
    SessionFactory sessionFactory = null;
    Session session = null;
    Transaction tx = null;
    try {
        sessionFactory = HibernateUtil.getSessionFactory();
        session = sessionFactory.openSession();
        tx = session.beginTransaction();

        User user = new User();
        user.setUsername("1530");
        user.setPassword("0521");
        user.setAddress("guangzhou");

        session.save(user);
        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    } finally {
        session.close();
        sessionFactory.close();
    }
}

Hibernate中session绑定本地线程

  SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程可以同时访问一个SessionFactory 并从中获取Session实例。而Session并非线程安全,也就是说,如果多个线程同时使用一个Session实例进行数据存取,则将会导致 Session 数据存取逻辑混乱.因此创建的Session实例必须在本地线程上运行,使之总与当前的线程相关。
参考:

http://blog.sina.com.cn/s/blog_7ffb8dd5010146i3.html
http://www.cnblogs.com/shipengzhi/articles/2102055.html

实现session与本地线程(当前线程)绑定

(1)在Hibernate的核心配置文件中配置

<property name="hibernate.current_session_context_class">thread</property>

(2)调用sessionFactory的getCurrentSession方法得到当前线程相关的sessio对象

public static Session getSession() {
    return sessionFactory.getCurrentSession();
}

注:获取与本地线程绑定的session对象后,手动关闭session会报错:org.hibernate.SessionException: Session was already closed。因为与本地线程绑定的session,在本地线程结束后就会被关闭,我们不需要在手动session.close()
HibernateUtil.java

package com.wm103.utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * Created by DreamBoy on 2017/5/19.
 */
public class HibernateUtil {
    // 单态模式的SessionFactory
    private static final SessionFactory sessionFactory;

    static {
        // 采用默认的hibernate.cfg.xml来启动一个Configuration的实例
        Configuration cfg = new Configuration().configure();
        sessionFactory = cfg.buildSessionFactory();
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    public static void main(String[] args) {
        System.out.println(getSessionFactory());
    }
}

测试案例:

@Test
public void testGetSession() {
    Session session = null;
    Transaction tx = null;
    try {
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();

        User user = new User();
        user.setUsername("1654");
        user.setPassword("0521");
        user.setAddress("guangzhou");

        session.save(user);
        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    }
}

Hibernate的其他API

(这里只演示查询表中所有数据的功能)

Query对象

  使用Query对象,我们不需要编写SQL语句,但是需要写HQL(HQL,Hibernate Query Language)语句。HQL语法类似于SQL,不同的是HQL是一种完全面向对象的语言,能够直接查询实体类及属性。而SQL语句操作的是数据库中的表和表中的字段。
查询表中所有数据的HQL:from 实体类名称。如:

@Test
public void testQuery() {
    Session session = null;
    Transaction tx = null;
    try {
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();

        Query query = session.createQuery("from User");
        List<User> list = query.list();
        for (User user : list) {
            System.out.println(user);
        }

        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    }
}

Criteria对象

@Test
public void testCriteria() {
    Session session = null;
    Transaction tx = null;
    try {
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();

        Criteria criteria = session.createCriteria(User.class);
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }

        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    }
}

SQLQuery对象

@Test
public void testSQLQuery() {
    Session session = null;
    Transaction tx = null;
    try {
        session = HibernateUtil.getSession();
        tx = session.beginTransaction();

        SQLQuery sqlQuery = session.createSQLQuery("SELECT * FROM t_user");
        // list方法默认返回的是一个List集合,且集合中的元素为对象数组Object[]
        /*List<Object[]> list = sqlQuery.list();
        for (Object[] obj : list) {
            System.out.println(Arrays.toString(obj));
        }*/

        // 返回一个List集合,且集合中的元素为实体类对象
        sqlQuery.addEntity(User.class);
        List<User> list = sqlQuery.list();
        for (User user : list) {
            System.out.println(user);
        }

        tx.commit();
    } catch (Exception e) {
        tx.rollback();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值