美文网首页Hibernate
JPA/Hibernate查询之Native SQL

JPA/Hibernate查询之Native SQL

作者: ilaoke | 来源:发表于2015-12-06 19:17 被阅读6123次

    http://jpwh.org/examples/

    Using native SQL queries

    当需要复杂查询时,直接写SQL必不可少。

    Automatic resultset handling

    Hibernate可以自动将SQL的查询结果封装成实体对象:

    List result = session.createSQLQuery("select * from CATEGORY")
                    .addEntity(Category.class)
                    .list();
    

    进一步,当有表连接时,指定查询某个实体:

    session.createSQLQuery("select {i.*} from ITEM i" +
                " join USERS u on i.SELLER_ID = u.USER_ID" +
                " where u.USERNAME = :uname")
            // 指定SQL查询结果要映射成哪个实体对象
            .addEntity("i", Item.class)
            .setParameter("uname", "johndoe");
    

    查询实体并初始化(eager fetch)关联实体或collection:

    session.createSQLQuery("select {i.*}, {u.*} from ITEM i" +
                    " join USERS u on i.SELLER_ID = u.USER_ID" +
                    " where u.USERNAME = :uname")
            .addEntity("i", Item.class)
            .addJoin("u", "i.seller")
            .setParameter("uname", "johndoe");
    

    addJoin()可解释为:i.seller关联属性将用别名为u的查询结果来立即初始化。

    Retrieving scalar values

    标量查询

    // 返回结果为: a List of Object[],数组中ITEM表的每一个字段
    List result = session.createSQLQuery("select * from ITEM").list();
    
    // 通过addScalar()指定scalar value
    session.createSQLQuery("select u.FIRSTNAME as fname from USERS u")
        .addScalar("fname");
    

    下例将字符串转成枚举类型:

    Properties params = new Properties();
    params.put("enumClassname", "auction.model.Rating");
    
    session.createSQLQuery("select c.RATING as rating from COMMENTS c where c.FROM_USER_ID = :uid")
            .addScalar("rating", Hibernate.custom(StringEnumUserType.class, params))
            .setParameter("uid", new Long(123));
    
    package auction.model;
    
    public enum Rating {
        EXCELLENT, OK, BAD;
    }
    
    @Entity
    @Table(name = "COMMENT")
    public class Comment implements Serializable, Comparable {
    
        @Id @GeneratedValue
        @Column(name = "COMMENT_ID")
        private Long id = null;
    
        @Enumerated(EnumType.STRING)
        @Column(name = "RATING", nullable = false, updatable = false)
        private Rating rating;
    }
    
    public class StringEnumUserType implements EnhancedUserType, ParameterizedType {
    
        private Class<Enum> enumClass;
    
        public void setParameterValues(Properties parameters) {
            String enumClassName = parameters.getProperty("enumClassname");
            try {
                enumClass = ReflectHelper.classForName(enumClassName);
            }
            catch (ClassNotFoundException cnfe) {
                throw new HibernateException("Enum class not found", cnfe);
            }
        }
    
        public Class returnedClass() {
            return enumClass;
        }
    
        public int[] sqlTypes() {
            return new int[] { Hibernate.STRING.sqlType() };
        }
    
        public boolean isMutable() {
            return false;
        }
    
        public Object deepCopy(Object value) {
            return value;
        }
    
        public Serializable disassemble(Object value) {
            return (Enum) value;
        }
    
        public Object replace(Object original, Object target, Object owner) {
            return original;
        }
    
        public Object assemble(Serializable cached, Object owner) {
            return cached;
        }
    
        public boolean equals(Object x, Object y) {
            return x==y;
        }
    
        public int hashCode(Object x) {
            return x.hashCode();
        }
    
        public Object fromXMLString(String xmlValue) {
            return Enum.valueOf(enumClass, xmlValue);
        }
    
        public String objectToSQLString(Object value) {
            return '\'' + ( (Enum) value ).name() + '\'';
        }
    
        public String toXMLString(Object value) {
            return ( (Enum) value ).name();
        }
    
        public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
                throws SQLException {
            String name = rs.getString( names[0] );
            return rs.wasNull() ? null : Enum.valueOf(enumClass, name);
        }
    
        public void nullSafeSet(PreparedStatement st, Object value, int index)
                throws SQLException {
            if (value==null) {
                st.setNull(index, Hibernate.STRING.sqlType());
            }
            else {
                st.setString( index, ( (Enum) value ).name() );
            }
        }
    
    }
    

    具体请下载原书代码

    最后,同时查询标量和实体对象:

    // 返回:a collection of Object[],数组中一个是Item对象,一个是字符串
    session.createSQLQuery("select {i.*}, u.FIRSTNAME as fname from ITEM i" 
                + " join USERS u on i.SELLER_ID = u.USER_ID" 
                + " where u.USERNAME = :uname")
            .addEntity("i", Item.class) // 实体
            .addScalar("fname")            // 标量
            .setParameter("uname", "johndoe");
    

    Native SQL in Java Persistence

    JPA使用createNativeQuery()创建SQL查询。

    A native SQL query may return entity instances, scalar values, or a mix of both.

    JPA如果要自动将SQL查询结果封装成实体对象,则必须查出实体所对应表的所有字段:

    // 返回 a collection of Category instances
    entityManager.createNativeQuery("select * from CATEGORY", Category.class);
    

    如果只是查询实体的某几个属性呢?

    entityManager.createNativeQuery("select " +
            "i.ITEM_ID as ITEM_ID, i.ITEM_PRICE as ITEM_PRICE, " +
            "u.USERNAME as USER_NAME, u.EMAIL as USER_EMAIL " +
            "from ITEM i join USERS u on i.SELLER_ID = u.USER_ID",
        "ItemSellerResult");
    

    需要@FieldResult来映射 实体属性和查询结果,以下映射信息放到实体中即可。

    @SqlResultSetMapping(
        name = "ItemSellerResult",
        entities = { 
            @EntityResult(
                entityClass = auction.model.Item.class, 
                fields = { 
                    @FieldResult(name = "id", column = "ITEM_ID"), 
                    @FieldResult(name = "initialPrice", column = "ITEM_PRICE") }), 
            @EntityResult(
                entityClass = auction.model.User.class,
                fields = { 
                    @FieldResult(name = "username", column = "USER_NAME"), 
                    @FieldResult(name = "email", column = "USER_EMAIL") }) 
         }
    )
    

    上面SQL的查询结果是a collection of Object[],数组中第一个是Item对象,第二个是User对象。

    如果查询的是标量scalar value:

    entityManager.createNativeQuery("select " +
            "i.ITEM_ID as ITEM_ID, count(b.*) as NUM_OF_BIDS " +
            "from ITEM i join BIDS b on i.ITEM_ID = b.ITEM_ID " +
            "group by ITEM_ID",
        "ItemBidResult");
    

    需要通过@ColumnResult来指定查询结果中的列名:

    @SqlResultSetMapping(
        name = "ItemBidResult",
        columns = {
            @ColumnResult(name = "ITEM_ID"),
            @ColumnResult(name = "NUM_OF_BIDS")
        }
    )
    

    上面SQL的查询结果依然是a collection of Object[],数组中是两个数值型值。

    当使用SQL来查询,总是希望能将查询结果,自动封装成业务对象,而不是通过硬编码将查询结果赋值给业务对象。对于JPA可以使用@EntityResult(参考这个例子),对于Hibernate可以使用ResultTransformer来完成自动转换。

    sqlQuery.setResultTransformer( Transformers.aliasToBean(ItemDTO.class) );
    

    此文是对《Java Persistence with Hibernate》第15章SQL部分的归纳。

    相关文章

      网友评论

        本文标题:JPA/Hibernate查询之Native SQL

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