Hibernate 快速入門3 - 批量處理和查詢
4 批量處理
4.1 批量插入
考慮一個(gè)批量插入100萬條學(xué)生數(shù)據(jù)到數(shù)據(jù)庫中。
我們當(dāng)然可以寫一個(gè)循環(huán),然后session.save(student)。(我看到一本書,給10萬的數(shù)據(jù)就OOM,我自己測(cè)試了100萬條,還是沒崩)
為了從節(jié)省內(nèi)存的角度出發(fā)以及避免可能的OOM,我們通常要手動(dòng)刷新session。
for (int i = 0; i < 1_000_000; i++) { Student student = new Student();
// ...
session.save(student);
if (i % 20 == 0) {
session.flush();
session.clear();
}
}
4.2 批量更新
想到最直接的是取出數(shù)據(jù)然后更新。然后我們知道SQL有批量更新的功能UPDATE r SET a WHERE P 。下面分別描述一下。
取出數(shù)據(jù)更新:
public static void batchUpdate() {
Configuration conf = new Configuration().configure();
StandardServiceRegistry registry = conf.getStandardServiceRegistryBuilder().build();
try (SessionFactory sessionFactory = conf.buildSessionFactory(registry); Session session = sessionFactory.openSession()) {
Transaction transaction = session.beginTransaction();
ScrollableResults users = session.createQuery("from Student")
.setCacheMode(CacheMode.IGNORE)
.scroll(ScrollMode.FORWARD_ONLY);
int count = 0;
while (users.next()) {
Student student = (Student) users.get(0); // fetch and update. 2 operations
student.setName("another name " + count);
if (++count % 20 == 0) {
session.flush();
session.clear();
}
}
transaction.commit();
}
}
批量更新:
public static void batchUpdateDML() {
Configuration conf = new Configuration().configure();
StandardServiceRegistry registry = conf.getStandardServiceRegistryBuilder().build();
try (SessionFactory sessionFactory = conf.buildSessionFactory(registry); Session session = sessionFactory.openSession()) {
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Student stud set name = :newName";
int nAffected = session.createQuery(hqlUpdate)
.setParameter("newName", "new name")
.executeUpdate();
tx.commit();
System.out.println(nAffected + " rows affected");
}
}
注意,上面createQuery中,表的名字必須是Class Name,還不能是@Entitye(name = "tableName")中指定的名字。
5 HQL查詢
HQL(Hibernate Query Language)和 SQL類似,但HQL是面向?qū)ο蟮牟樵冋Z言。SQL的操作對(duì)象是數(shù)據(jù)表、列等數(shù)據(jù)庫對(duì)象,而HQL的操作對(duì)象是類、實(shí)例、屬性等。大體和SQL是差不多的,這里就只簡單介紹一下。
5.1 HQL查詢
步驟:
- 獲取Hibernate Session對(duì)象
- 編寫HQL語句
- 已HQL語句作為參數(shù),調(diào)用Session.createQuery()
- 如果HQL語句包含參數(shù),調(diào)用Query.setXxx()方法為參數(shù)賦值
- 調(diào)用Query.list() 或 Query.uniqueResult()方法返回查詢結(jié)果列表。像上一節(jié)中一樣,可以用游標(biāo)ScrollableResults。
HQL具體語法請(qǐng)參考官網(wǎng) https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/queryhql.html#queryhql-select
直接用FROM 子句。這也是最簡單的
public static void findStudent() {
Configuration configuration = new Configuration().configure();
StandardServiceRegistry registry = configuration.getStandardServiceRegistryBuilder().build();
try (SessionFactory factory = configuration.buildSessionFactory(registry); Session session = factory.openSession()) {
Transaction tx = session.getTransaction();
tx.begin();
List list = session.createQuery("from Student AS s WHERE s.age > ?")
.setParameter(0, 17)
.list();
for (Object o : list) {
Student s = (Student) o;
System.out.println(s.getId());
}
tx.commit();
}
}
用SELECT 子句。
public static void selectStudent() {
Configuration conf = new Configuration().configure();
StandardServiceRegistry registry = conf.getStandardServiceRegistryBuilder().build();
try (SessionFactory factory = conf.buildSessionFactory(registry); Session session = factory.openSession()) {
Transaction tx = session.getTransaction();
tx.begin();
List list = session.createQuery("SELECT stud FROM Student AS stud WHERE stud.age > ?")
.setParameter(0, 20)
.list();
for (Object o: list) {
Student s = (Student) o;
System.out.println(s.getId());
}
tx.commit();
}
}
注意: 查詢也會(huì)返回子類。這里是PostGraduate。
5.2 關(guān)聯(lián)連接
隱式連接:
FROM Student AS stud WHERE stud.name.someAttr = :someValue
FROM Student AS stud WHERE stud.addresses.zipCode = 12345
假設(shè)addresses是個(gè)集合屬性(另外在一個(gè)表上), Hibernate會(huì)自動(dòng)執(zhí)行JOIN操作。
顯示連接:
FROM Student AS stud INNER JOIN p.addresses AS addrList WHERE addrList.zipCode = 12345
JOIN還有LEFT OUTER JOIN, RIGHT OUTER JOIN
5.3 聚集函數(shù)
SELECT MAX(stud.age) FROM Student AS stud
5.3 條件查詢
條件查詢是更具面向?qū)ο筇厣臄?shù)據(jù)查詢方式。條件查詢通過如下三個(gè)類完成。
- Criteria:代表一次查詢
- Criterion:代表一個(gè)查詢條件
- Restrictions:產(chǎn)生查詢條件的工具類
public static void selectStudentByCriteria() {
Configuration conf = new Configuration().configure();
StandardServiceRegistry registry = conf.getStandardServiceRegistryBuilder().build();
try (SessionFactory factory = conf.buildSessionFactory(registry); Session session = factory.openSession()) {
Transaction tx = session.getTransaction();
tx.begin();
List list = session.createCriteria(Student.class)
.add(Restrictions.gt("age", 20))
.list();
for (Object o: list) {
Student student = (Student) o;
System.out.println(student.getId());
}
tx.commit();
}
}
Session.createCriteria已經(jīng)被標(biāo)記為Deprecated,建議采用JPA的Criteria。所以暫時(shí)先不仔細(xì)說這個(gè)。
5.4 SQL查詢
Hibernate支持原生SQL查詢。
5.4.1 基本查詢
// 返回student_info表中所有數(shù)據(jù)
session.createSQLQuery("SELECT * from student_info").list()
Hibernate會(huì)通過ResultSetMetadata來判定所返回?cái)?shù)據(jù)列的實(shí)際順序和類型。
JDBC中過多使用ResultSetMetadata會(huì)降低性能。建議指明數(shù)據(jù)列的類型,這可以通過addScalar()方法來實(shí)現(xiàn)。
要注意SQL查詢和HQL查詢,中table名字的指定。HQL中指定的是類名,SQL中必須指定數(shù)據(jù)庫中的表名。
List list = session.createSQLQuery("SELECT * FROM student_info")
.addScalar("age", StandardBasicTypes.INTEGER)
.addScalar("id", StandardBasicTypes.STRING)
.list()
上面指明了age的數(shù)據(jù)類型是INTEGER, id類型是string同時(shí),Hibernate也只會(huì)返回這兩個(gè)column的值。省略第二個(gè)參數(shù)(類型)的意思是只返回對(duì)應(yīng)column,不指明column類型。
返回的結(jié)果是標(biāo)量(數(shù)值)列表,僅僅是一些值的組合,而不是一個(gè)Entity。
意思是,對(duì)于結(jié)果,我們應(yīng)該這樣取出來:
for (Object o: list) {
Object[] row = (Object[]) o;
System.out.println("id: " + row[0] + ", name: " + row[1]);
}
5.4.2 實(shí)體查詢
為了讓返回的結(jié)果是Entity,即能轉(zhuǎn)換為對(duì)應(yīng)類的對(duì)象??梢哉{(diào)用SQLQuery.addEntity()。
List list = session.createSQLQuery("SELECT * FROM student_info")
.addEntity(Student.class)
.addScalar("age", StandardBasicTypes.INTEGER)
.addScalar("id", StandardBasicTypes.STRING)
.list()
for (Object o: list) {
Student stud = (Student)o;
System.out.println("id: " + stud.getId() + ", name: " + stud.getName());
}
5.4.3 命名SQL查詢 (NamedNativeQuery)
命名SQL查詢作用于類上。
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "student_info")
@NamedNativeQuery(name = "simpleNamedQuery", query = "SELECT stud.id, stud.name FROM student AS stud",
resultClass = Student.class)
public class Student {
@Id
@GenericGenerator(name = "student_uuid", strategy = "org.hibernate.id.UUIDGenerator")
@GeneratedValue(generator = "student_uuid")
private String id;
private String name;
private int age;
}
public class SQLQuery {
public static void namedSqlQuery() {
Configuration conf = new Configuration().configure();
StandardServiceRegistry registry = conf.getStandardServiceRegistryBuilder().build();
try (SessionFactory factory = conf.buildSessionFactory(registry); Session session = factory.openSession()) {
Transaction tx = session.getTransaction();
List list = session.getNamedNativeQuery("simpleNamedQuery")
.list();
for (Object o: list) {
Student student = (Student) o;
System.out.println("id: " + student.getId() + ", name: " + student.getName());
}
tx.commit();
}
}
}
關(guān)于@NamedNativeQuery請(qǐng)參考官方文檔。
5.4.4 定制SQL
通過定制SQL可以擴(kuò)展或者完全改變Hibernate底層持久化的所用的SQL語句。
@SQLInsert(sql="INSERT INTO student_info(name, age) VALUES(UPPER(?), ?")
public class Student {
}
插入Student對(duì)象時(shí),SQL就會(huì)使用我們指定的SQL語句。
同樣還有更新,刪除對(duì)應(yīng)的: @SQLUpdate, @SQLDelete, @SQLDeleteAll
6 數(shù)據(jù)郭略
數(shù)據(jù)過濾使用 @FilterDef, @Filter。我個(gè)人覺得在SQL中指明過濾更好。所以,暫時(shí)先不講這個(gè)。