美文网首页
Hibernate入门3-批量操作

Hibernate入门3-批量操作

作者: sunblog | 来源:发表于2018-05-09 01:31 被阅读0次

    Hibernate 快速入门3 - 批量处理和查询

    4 批量处理

    4.1 批量插入

    考虑一个批量插入100万条学生数据到数据库中。

    我们当然可以写一个循环,然后session.save(student)。(我看到一本书,给10万的数据就OOM,我自己测试了100万条,还是没崩)

    为了从节省内存的角度出发以及避免可能的OOM,我们通常要手动刷新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 批量更新

    想到最直接的是取出数据然后更新。然后我们知道SQL有批量更新的功能UPDATE r SET a WHERE P 。下面分别描述一下。

    取出数据更新:

    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是面向对象的查询语言。SQL的操作对象是数据表、列等数据库对象,而HQL的操作对象是类、实例、属性等。大体和SQL是差不多的,这里就只简单介绍一下。

    5.1 HQL查询

    步骤:

    1. 获取Hibernate Session对象
    2. 编写HQL语句
    3. 已HQL语句作为参数,调用Session.createQuery()
    4. 如果HQL语句包含参数,调用Query.setXxx()方法为参数赋值
    5. 调用Query.list() 或 Query.uniqueResult()方法返回查询结果列表。像上一节中一样,可以用游标ScrollableResults。

    HQL具体语法请参考官网 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();
        }
    }
    

    注意: 查询也会返回子类。这里是PostGraduate。

    5.2 关联连接

    隐式连接:

    FROM Student AS stud WHERE stud.name.someAttr = :someValue
    
    FROM Student AS stud WHERE stud.addresses.zipCode = 12345
    

    假设addresses是个集合属性(另外在一个表上), Hibernate会自动执行JOIN操作。

    显示连接:

    FROM Student AS stud INNER JOIN p.addresses AS addrList WHERE addrList.zipCode = 12345
    

    JOIN还有LEFT OUTER JOIN, RIGHT OUTER JOIN

    5.3 聚集函数

    SELECT MAX(stud.age) FROM Student AS stud
    

    5.3 条件查询

    条件查询是更具面向对象特色的数据查询方式。条件查询通过如下三个类完成。

    • Criteria:代表一次查询
    • Criterion:代表一个查询条件
    • Restrictions:产生查询条件的工具类
    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已经被标记为Deprecated,建议采用JPA的Criteria。所以暂时先不仔细说这个。

    5.4 SQL查询

    Hibernate支持原生SQL查询。

    5.4.1 基本查询

    // 返回student_info表中所有数据
    session.createSQLQuery("SELECT * from student_info").list()
    

    Hibernate会通过ResultSetMetadata来判定所返回数据列的实际顺序和类型。

    JDBC中过多使用ResultSetMetadata会降低性能。建议指明数据列的类型,这可以通过addScalar()方法来实现。

    要注意SQL查询和HQL查询,中table名字的指定。HQL中指定的是类名,SQL中必须指定数据库中的表名。

    List list = session.createSQLQuery("SELECT * FROM student_info")
            .addScalar("age", StandardBasicTypes.INTEGER)
            .addScalar("id", StandardBasicTypes.STRING)
            .list()
    

    上面指明了age的数据类型是INTEGER, id类型是string同时,Hibernate也会返回这两个column的值。省略第二个参数(类型)的意思是返回对应column,不指明column类型。

    返回的结果是标量(数值)列表,仅仅是一些值的组合,而不是一个Entity。

    意思是,对于结果,我们应该这样取出来:

    for (Object o: list) {
        Object[] row = (Object[]) o;
        System.out.println("id: " + row[0] + ", name: " + row[1]);
    }
    

    5.4.2 实体查询

    为了让返回的结果是Entity,即能转换为对应类的对象。可以调用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();
            }
        }
    }
    

    关于@NamedNativeQuery请参考官方文档。

    5.4.4 定制SQL

    通过定制SQL可以扩展或者完全改变Hibernate底层持久化的所用的SQL语句。

    @SQLInsert(sql="INSERT INTO student_info(name, age) VALUES(UPPER(?), ?")
    public class Student {
    }
    

    插入Student对象时,SQL就会使用我们指定的SQL语句。

    同样还有更新,删除对应的: @SQLUpdate, @SQLDelete, @SQLDeleteAll

    6 数据郭略

    数据过滤使用 @FilterDef, @Filter。我个人觉得在SQL中指明过滤更好。所以,暂时先不讲这个。

    相关文章

      网友评论

          本文标题:Hibernate入门3-批量操作

          本文链接:https://www.haomeiwen.com/subject/embgrftx.html