Hibernate入門3-批量操作

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查詢

步驟:

  1. 獲取Hibernate Session對(duì)象
  2. 編寫HQL語句
  3. 已HQL語句作為參數(shù),調(diào)用Session.createQuery()
  4. 如果HQL語句包含參數(shù),調(diào)用Query.setXxx()方法為參數(shù)賦值
  5. 調(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è)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Hibernate: 一個(gè)持久化框架 一個(gè)ORM框架 加載:根據(jù)特定的OID,把一個(gè)對(duì)象從數(shù)據(jù)庫加載到內(nèi)存中OID...
    JHMichael閱讀 2,100評(píng)論 0 27
  • 本文包括:1、Hibernate 的查詢方式2、HQL (Hibernate Query Language) 查詢...
    廖少少閱讀 2,725評(píng)論 0 15
  • 本文中我們介紹并比較兩種最流行的開源持久框架:iBATIS和Hibernate,我們還會(huì)討論到Java Persi...
    大同若魚閱讀 4,457評(píng)論 4 27
  • 你最近一次稱贊別人是幾時(shí)?
    幺妹兒oo閱讀 176評(píng)論 1 2
  • 素心佛手柑味昋, 食材為吾添善意, 生生不息眾所愿, 命有平安舒心結(jié)。
    理療瑜伽邵閱讀 231評(píng)論 0 0

友情鏈接更多精彩內(nèi)容