传统艺能增删改查开始了,学完这个,就可以写基础的应用了。
两个基础概念
SessionFactory,这是Hibernate通过配置文件创建的一个对象,每个Web应用里只有一个。可以理解为像连接池一样,这个SessionFactory 相当于是一个Session池。
Session,与Web开发的Session不同,每个Session对象里边装着一个JDBC连接,是用来增删改查的对象,生存时间很短,需要的时候就从SessionFactory中获取。
Create 添加对象
通过这个例子也可以一并了解创建SessionFactory和使用session的方法:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class TestSessionFactory {
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Student.class).buildSessionFactory();
Session session = factory.getCurrentSession();
try {
//创建一个新的Student对象
Student newStudent = new Student("Cony", "Li", "Conyli@gmail.com");
//开始一个事务
session.beginTransaction();
//将Student对象保存进数据库
session.save(newStudent);
//提交事务
session.getTransaction().commit();
} finally {
factory.close();
}
}
}
首先是创建SessionFactory的语句,如果configure()
的参数是为空,Hibernate会自动寻找名称叫hibernate.cfg.xml
的配置文件,但一般还是显式给出文件名较好。
.addAnnotatedClass(Student.class)
这个方法用于添加被注解成Entity Class的类,其实就是将SessionFactory绑定到一个具体的表,之后的操作都是针对这个表。
创建好SessionFactory对象之后,使用.getCurrentSession()
获取Session对象,之后就通过session对象操作数据库。
在使用完session对象后,要关闭。
在try语句中,创建一个新的Student对象,然后开始事务,写好执行代码后提交事务即可。这里如果添加上打印语句可以看到,在刚创建newStudent的时候,成员变量id被初始化在0,在写入数据库之后,id就有了值,对应数据库中的主键值。
Hibernate与主键
继续CRUD之前,先来看一下主键
主键已经都知道了,是用于区分一个表中的不同行,是一个int类型的数据,不能够是null。
一般主键定义了之后,都由数据库自行控制,每次表内有变化,这个值都会增加。但Hibernate也可以控制这个主键的一些策略,在刚才的Student对象中,可以加上一个注解:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;
这个注解表示设置id生成的方式为IDENTITY,也就是交给数据库去生成。其他一些自动生成策略如下:
ID Generations Strategies
名称 |
解释 |
GenerationType.AUTO |
针对不同的数据自动选取适合的策略 |
GenerationType.IDENTITY |
使用数据库对于主键列的配置 |
GenerationType.SEQUENCE |
根据数据库的sequence进行操作,比如Oracle数据库就有sequence这个概念 |
GenerationType.TABLE |
使用一个数据表中的内容来确保唯一性生成id |
还可以创建自己的生成ID的策略,需要继承org.hibernate.id.SequenceGenerator
,然后重写generate(...)
方法,需要注意的是,需要保证每次都生成一个独特的值。这里不再深入。
通过主键获取具体对象
使用session.get()方法来查询数据,查询的结果是一个Entity Class对象。
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class TestRetrieve {
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Student.class).buildSessionFactory();
Session session = factory.getCurrentSession();
int primaryKey = 133;
try {
session.beginTransaction();
Student student = session.get(Student.class, primaryKey);
session.getTransaction().commit();
if (student == null) {
System.out.println("Not found");
} else {
System.out.println(student);
}
} finally {
factory.close();
}
}
}
这里使用了session>get(Class, 主键)
的获取方法,第一个参数是Entity Class的类文件,第二个参数是主键。
还需要注意的是,.get()
方法也必须在一个事务里提交。
如果找不到数据,则返回一个null。
Retrieve 查询对象
刚才是通过主键获取对象,比较简单直接,但是只能用在知道主键的情况下。而更加一般的查询语句如下:
List<Student> students =session.createQuery("FROM Student s where s.lastName='Li'").getResultList();
.createQuery()
方法中的参数非常类似于SQL的查询,只不过用Java中的类型代替了SQL中的表名,直接用取属性代替了列名。
这其中还可以使用类似SQL的OR语句或者like语句,编写一段代码试验一下:
try {
session.beginTransaction();
List<Student> students = session.createQuery("FROM Student s where s.lastName='Li'").getResultList();
List<Student> students2 = session.createQuery("FROM Student s where s.lastName='Li' or s.firstName='Jenny'").getResultList();
List<Student> students3 = session.createQuery("FROM Student s where s.email like '%sina.com'").getResultList();
session.getTransaction().commit();
for(Student s: students){
System.out.println(s);
}
System.out.println("----------------------------------------------");
for(Student s: students2){
System.out.println(s);
}
System.out.println("----------------------------------------------");
for(Student s: students3){
System.out.println(s);
}
}finally {
factory.close();
}
注意,在Hibernate 5.2及更高版本中,需要使用.getResultList()
方法,老的.list()
已经被废弃。
Update 修改数据
修改数据有两种方法,一种是先获取一个具体对象,然后修改该对象的数据;一种是直接通过.createQuery()
来执行类似SQL语句的更新。
第一种方法,Hibernate修改数据之后无需调用.save()
方法,直接提交事务即可。例子如下:
public static void main(String[] args) {
SessionFactory factory = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Student.class).buildSessionFactory();
Session session = factory.getCurrentSession();
int primaryKey = 3;
try {
session.beginTransaction();
Student student = session.get(Student.class, primaryKey);
System.out.println("修改前的数据是 " + student);
student.setEmail("lee0709@vip.sina.com");
student.setLastName("Lee");
session.getTransaction().commit();
System.out.println("修改后的数据是 " + student);
} finally {
factory.close();
}
}
这种方式无需再调用save方法,直接提交事务就可以。另外一种方法:
try {
session.beginTransaction();
session.createQuery("update Student s set s.email='test@gmail.com' where s.id=2").executeUpdate();
session.getTransaction().commit();
} finally {
factory.close();
}
这种方法和查询的时候比较类似,采用的方式很像执行一个SQL语句,其中的属性名和列名都用对象属性来表示。.executeUpdate()
返回一个int值,表示受影响的行数。
两种方法其实推荐第一种,因为比较像传统的面向对象的Java代码。
Delete 删除数据
删除数据和查询一样,也是有面向对象操作和提交Query操作两种方式,先来看面向对象操作:
int pk = 5;
try {
session.beginTransaction();
Student student = session.get(Student.class, pk);
session.delete(student);
session.getTransaction().commit();
} finally {
factory.close();
}
面向对象操作也需要在事务中进行操作,.delete()
的返回值是void。在提交完事务之后,数据库中已经没有了student对应的这一行,但是student的数据依然可以使用,因为这个对象还在内存中。
提交Query方式:
try {
session.beginTransaction();
session.createQuery("delete Student s where s.id=7").executeUpdate();
session.getTransaction().commit();
}finally {
factory.close();
}
同更新数据一样都执行.executeUpdate()
,也会返回受影响的行数。
至此搞定了简单的增删改查。如果说Web开发的传统艺能之一是操作ORM进行增删改查,那么ORM的传统艺能就是操作约束关系了。