Hibernate初探之单表映射
来源:互联网 发布:mac截图怎么保存 编辑:程序博客网 时间:2024/05/24 07:13
第1章 Hibernate初始
什么是ORM
ORM(Object/RelationShip Mapping):对象/关系映射。利用面向对象思想编写的数据库应用程序最终是把对象的信息保存在关系型数据库中,于是要编写很多与底层数据库相关的Sql语句,而且:不同的数据库使用的SQL语法不同,比如PL/SQL与T/SQL。
Hibernate是一个开放源代码的对象关系映射框架它对JDBC进行了非常轻量级的对象封装。
使用Hibernate需要有以下4步:
- 创建Hibernate的配置文件hibernate.cfg.xml
- 创建持久化类
- 创建对象-关系映射文件
- 通过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&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的规范:
- JavaBean 必须申明为 public class 即:必须是公有的类
- JavaBean 的所有属性必须申明为 private 即:属性必须私有
- 通过 setter 方法和 getter 方法设值和取值
- 必须有一个公有无参构造方法
创建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常用配置
session简介
hibernate的执行流程
- 创建配置对象Configuration,配置对象的作用就是读取配置文档Hibernate.cfg.xml
- 获得Configuration的目的是创建SessionFactory对象。创建SessionFactory时就会读取相应的里面所加载的对象关系映射文件。
- 获得了SessionFactory对象后就可创建Session对象。Session对象类似于JDBC中的Connection。获得了Session对象,就表示获得了数据库连接对象。就可以执行Session中相应的方法,如:save、delecte、update、get。
- 在执行Session的方法时,必须要开启事务。这些方法都要封装在事务当中。
- 执行完方法后,要先提交事务,再关闭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对象?
- openSession
- 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的区别
- getCurrentSession在事务提交或者回滚之后会自动关闭,而openSession需要手动关闭。如果使用openSession而没有手动关闭,多次提交之后会导致连接池溢出。
- 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"/>
,主键生成策略
主键生成策略
Hibernate单表操作
单一主键
单一主键是指表中由某一列充当主键。
两种常见的生成但已逐渐策略:
- assigned 由java应用程序负责生成(手工赋值)。
- native 由底层数据库自动生成标识符,如果是MySQL就是increment,如果是Oracle就是sequence。
hibernate基本类型
日期类型的详细映射关系
前面例子中数据库中BIRTHDAY字段是带时分秒的,如果想只显示为日期,则要在Student.hbm.xml文件中把birthday属性的type该为Hibernate映射类型中的date就可以了
<property name="birthday" type="date"> <column name="BIRTHDAY"></column></property>
对象类型
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(); }
这两个测试方法中遇到了两个问题:
- 测试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>
- 解决了上面问题后,再次执行测试方法,测试未通过。显示:
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的区别
- 在不考虑缓存的情况下,get方法会在调用之后立即向数据库发出sql语句,返回持久化对象。load方法会在调用后返回一个代理对象。该代理对象只保存了实体对象的id,直到使用对象的非主键属性时才会发出sql语句。
- 查询数据库中不存在数据时,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 }
- 慕课网 hibernate初探之单表映射
- Hibernate初探之单表映射
- Hibernate初探之单表映射
- Hibernate初探之单表映射笔记(1)
- Hibernate初探之单表映射笔记(2)
- 慕课网-Hibernate初探之单表映射 学习
- Hibernate初探之单表映射笔记(2)
- Hibernate 之单表映射
- Hibrenante初探之单表映射
- hibernate初探之表单映射
- Hibernate 之单表继承映射策略
- hibernate单表之组件映射和继承映射
- hibernate单表映射
- Hibernate单表映射
- hibernate单表映射
- Hibernate单表映射
- hibernate初探之单向一对多映射
- hibernate单表继承映射
- leetcode 第一题:使用哈希表解决
- 141.linked list cycle
- Time To Get Up
- 使用VS2015的Visual Studio Installer打包
- Spring 集成JediisCluster【redis使用】
- Hibernate初探之单表映射
- Java-组个最小数 (20)
- bootstrap下拉栏
- BZOJ 4974: 字符串大师 KMP
- 深度学习-RNN网络的理解
- hdoj1087超级跳跳跳 最大升序字段和
- gulp-connect-php 配置
- GET方式请求表单的action属性后不能带参数
- 关于MySQL AUDIT(审计)那点事