简介
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。
即:全自动非常方便,数据持久化,但由于是全自动,如果想要自定义功能会非常麻烦。
配置
1.jar包
这些是必须要导入的 jar 包,其他的可以在官网下载的文件里面自行选择。
下面是我整个项目的 jar 包
2.配置文件
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">
<hibernate-configuration>
<!--hibernate.可以省略,hibernate会做判断,如 hibernate.c3p0.max_size可写成c3p0.max_size -->
<session-factory>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///study?serverTimezone=GMT%2B8</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 指定连接池里最大连接数 -->
<property name="hibernate.c3p0.max_size">20</property>
<!-- 指定连接池里最小连接数 -->
<property name="hibernate.c3p0.min_size">1</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout">5000</property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.c3p0.validate">true</property>
<!--指定数据库名,mysql低版本曾用hibernate.default_schema表示数据库名 -->
<property name="hibernate.default_catalog">study</property>
<!-- 指定数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 显示Hibernate持久化操作所生成的SQL -->
<property name="show_sql">true</property>
<!-- 将SQL脚本进行格式化后再输出 -->
<property name="hibernate.format_sql">true</property>
<!-- 测试环境使用create/create-drop 正式环境使用update/validate -->
<property name="hbm2ddl.auto">update</property>
<!--扫描实体包 -->
<mapping class="com.abc.entity.Student" />
<mapping class="com.abc.entity.Customer" />
<mapping class="com.abc.entity.Order" />
</session-factory>
</hibernate-configuration>
log4j.properties 日志信息输送文件
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=D:\\log\\hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
# DEBUG,INFO,WARN,ERROR,FATAL
log4j.rootLogger=ERROR, stdout
现在是控制台打印日志信息,注释掉的是输出日志信息到文件。
3.工具类
HibernateUtil 类
public class HibernateUtil {
private static final SessionFactory SESSION_FACTORY;
static {
try {
SESSION_FACTORY = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
public static Session getSession() {
return SESSION_FACTORY == null ? null : SESSION_FACTORY.openSession();
}
public static void close(Session ses) {
if (ses != null) {
ses.close();
}
}
}
HibernateTemplate 类
public class HibernateTemplate {
public static final Object execute(HibernateCallback hc) {
Session ses = null;
Transaction tx = null;
Object obj = null;
try {
ses = HibernateUtil.getSession();
tx = ses.beginTransaction();
obj = hc.doHibernate(ses);
tx.commit();
} catch (HibernateException e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
} finally {
HibernateUtil.close(ses);
}
return obj;
}
}
HibernateCallback 类
public interface HibernateCallback {
Object doHibernate(Session ses) throws HibernateException;
}
应用注解将实体类与数据表建立映射关系
1.一张独立的表 student:id,name,birthday,gender(0,1)
Student 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
//表示当前类是一个实体类
@Entity
//表示该实体类对应一张表
@Table(name = "student")
//动态更新和添加
@DynamicUpdate
@DynamicInsert
public class Student {
//主键
@Id
//主键生成策略
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//列设置,非空唯一
@Column(unique = true, nullable = false)
private String name;
@Column
private LocalDate birthday;
@Column
@Enumerated(EnumType.ORDINAL)
private Gender gender;
}
Gender 是枚举
public enum Gender {
MALE, FEMALE;
}
2.如果表有关联关系,可以实现关联
比如:顾客和订单是一对多的关系
顾客表customer:id,name
订单表order:id,orderno,price,customer_id
Customer 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 一对多
// 配置级联 cascade
// 加载模式 fetch
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Order> orders;
}
Order 实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo;
private Double price;
// 多对一
@ManyToOne
// 外键
@JoinColumn(name = "customer_id", referencedColumnName = "id")
private Customer customer;
@Override
public String toString() {
return "Order [id=" + id + ", orderNo=" + orderNo + ", price=" + price + "]";
}
}
关于Hibernate框架
HQL—— Hibernate Query Language
* 它是Hibernate框架提供的一种半面向对象,半面向关系的查询语句,所以性能较高
* hql中出现的都是类名以及属性名,但是它本质上还是一种SQL语句.
*
* 补充:
* Hibernate官方其实提供了三种不同的方式进行查询:
* 1.原生SQL 直接使用原生SQL来进行查询
* 2.HQL 半面向对象,半面向关系的查询语句(推荐)
* 3.QBC 纯面向对象,完全不用写HQL和SQL,性能低于SQL和HQL方式
Hibernate在级联查询时,会根据级联情况自动提供加载模式:
* 一对一:默认采用迫切加载
* 一对多:默认多的一方采用懒加载
* 多对一:默认一的一方采用迫切加载
* 多对多:默认采用懒加载
用Hibernate框架操作数据库(增删改查)
注意:以下代码全部采用 lambda 表达式(匿名内部类重写方法)
当操作代码运行时,如果表存在,就直接操作;
如果表不存在,会先根据映射关系建表,再进行操作。
但是,它在建表时,非主键列并不是按照顺序排列,所以,自动创建的表可能不太理想。
如果可以的话,就手动建表。当然,如果对这无所谓,那就随心所欲、为所欲为吧。
1.对一张表进行操作
StudentDao 接口
public interface StudentDao {
/**
* 添加学生
*/
long insertStudent(Student student);
/**
* 批量添加多个学生
*/
void insertBatchStudents(List<Student> students);
/**
* 删除学生
*/
void deleteStudentById(Student student);
/**
* 根据姓名删除学生(模糊)
*/
int deleteStudentsByName(String name);
/**
* 修改学生
*/
void updateStudent(Student student);
/**
* 查询学生
*/
Student selectStudentById(Long id);
List<Student> selectAllStudents();
List<Student> selectStudentsByName(String name);
}
StudentDaoImpl 实现类
public class StudentDaoImpl implements StudentDao {
@Override
public long insertStudent(Student student) {
return (long) HibernateTemplate.execute(ses -> ses.save(student));
}
@Override
public void insertBatchStudents(List<Student> students) {
HibernateTemplate.execute(ses -> {
for (Student student : students) {
ses.save(student);
}
return null;
});
}
@Override
public void deleteStudentById(Student student) {
HibernateTemplate.execute(ses -> {
ses.delete(student);
return null;
});
}
@Override
public int deleteStudentsByName(String name) {
return (int) HibernateTemplate.execute(ses -> ses.createQuery("delete from Student where name like :name")
.setParameter("name", "%" + name + "%").executeUpdate());
}
@Override
public void updateStudent(Student student) {
HibernateTemplate.execute(ses -> {
ses.update(student);
return null;
});
}
@Override
public Student selectStudentById(Long id) {
return (Student) HibernateTemplate.execute(ses -> ses.get(Student.class, id));
}
@SuppressWarnings("unchecked")
@Override
public List<Student> selectAllStudents() {
return (List<Student>) HibernateTemplate.execute(ses -> ses.createQuery("from Student").list());
}
@SuppressWarnings("unchecked")
@Override
public List<Student> selectStudentsByName(String name) {
return (List<Student>) HibernateTemplate.execute(ses -> ses.createQuery("from Student where name like :name")
.setParameter("name", "%" + name + "%").list());
}
}
其中,createQuery() 方法的参数语句就是上面提到的 HQL 语句,里面需要设置的参数用 “:名字”,后面跟设置参数方法 setParameter(“名字”,变量) 。最后一个结果用 uniqueResult() ,多个结果用 list() 。
从上面代码可以看出,每个方法的代码都差不多,并且代码量很少,这都源于 Hibernate 框架的强大功能。
2.对互相关联的表进行级联操作
CustomerDao 接口
public interface CustomerDao {
/**
* 级联插入数据
* 添加顾客的同时,如果顾客有订单信息,则级联添加订单信息
*/
void insertCustomerAndOrders(Customer customer);
/**
* 级联查询方法[懒加载,迫切加载]
* 查询顾客信息,如果该顾客有订单信息,则级联查询订单信息
*/
Customer selectCustomerAndOrdersById(Long id);
}
CustomerDaoImpl 实现类
public class CustomerDaoImpl implements CustomerDao {
@Override
public void insertCustomerAndOrders(Customer customer) {
HibernateTemplate.execute(ses -> ses.save(customer));
}
@Override
public Customer selectCustomerAndOrdersById(Long id) {
return (Customer) HibernateTemplate.execute(
ses -> ses.createQuery("select c from Customer c join Order o on c.id=o.customer.id where c.id=:id")
.setParameter("id", id).uniqueResult());
}
}
其中,select 语句比 from 语句提供了更多的对结果集的控制。
级联插入只用了一行代码,只要映射关系写好了,Hibernate 会自动把数据进行级联插入。而级联查询根据其 HQL 语句可分为 显式关联 和 隐式关联 。
比如:
OrderDao 接口
public interface OrderDao {
/**
* 根据顾客名字查询其订单
*/
List<Order> selectOrdersByCustomerName(String name);
}
OrderDaoImpl 实现类
public class OrderDaoImpl implements OrderDao {
@SuppressWarnings("unchecked")
@Override
public List<Order> selectOrdersByCustomerName(String name) {
// 显式关联
return (List<Order>) HibernateTemplate.execute(
ses -> ses.createQuery("select o from Order o join Customer c on o.customer.id=c.id where c.name=:name")
.setParameter("name", name).list());
// 隐式关联
return (List<Order>) HibernateTemplate.execute(
ses -> ses.createQuery("from Order o where o.customer.name=:name").setParameter("name", name).list());
}
}
总结
Hibernate 框架的功能还有很多,这里我只写了暂时用到的一部分,还有许多其他更加强大的功能需要慢慢摸索。
前路漫长,未来可期