hibernate高级应用

来源:互联网 发布:夏日大作战 知乎 编辑:程序博客网 时间:2024/05/30 04:48

DML风格的批量更新和删除

语法格式:
delete | update from? [Where condition]
from是可选的,可以不写。from字句后面只能有一个类名。可以指定别名。
可以使用where字句查询但不能使用连接。

String hqlUpdate = "update User u set name=:newName";int updateEntities = session.creaateQuery(hqlUpdate).setString("newName","新名字").executeUpdate();String hqlDelete = "delete User";int deleteEntities = session.createQuery(hqlDelete).executeUpdate();

HQL查询

  1. 获取session对象
  2. 编写HQL语句
  3. session.createQuery(hql)创建查询对象
  4. 调用Query.setXXX方法设置查询参数
  5. 调用Query对象的list()或uniqueResult()返回查询结果。
List p1 = session.createeQuery("select distinct p from Person p            join p.myEvents where title = :eventTitle")            .setString("eventTitle","很普通")            .list();List p2 = session.createeQuery("select distinct p from Person p            inner join p.myEvents events where event.happenDate between :firstDate and :endDate")            .setDate("firstDate",new Date())            .setDate("lastDate",new Date())            .list();

条件查询

  • Creteria:代表依次查询
  • Criterion:代表一个查询条件
  • Restrictions:产生查询条件的工具类。
    执行条件查询的步骤:
    1. 获得session
    2. session创建criteria对象。
    3. 使用Restrictions的静态方法创建Criterion对象。
    4. 向Criteria对象添加Criterion查询条件。
      5 执行Criteria的list()或uniqueResult()。
List list = session.createCriteria(Student.class)                    .add(Restrictions.gt("name","a"))                    .list():

Cretiria还有如下方法:
setFirstResult(int);
setMaxResult(int);
addOrder(Order order)

分组、投影、聚合

使用Projection代表投影运算。Projection是一个接口,而Projections作为Projection的 工厂。负责生产Projection对象。

List cats = session.createCriteria(Cat.class)                    .setProjection(Projections.projectionList()                    .add(Projection.rowCOunt())                    .add(Projection.avg("weight"))                    .add(Projection.groupProperty("color"))                    .addOrder(Order.asc("color"))                    )                    .list();

离线查询和子查询

使用DetachedCriteria实现。

//离线查询DetachedCriteria query = DetachedCriteria            .forClass(Student.class)            .setProjection(Property.forName("name"));        Session session = HibernateUtil.currentSession();        Transaction tx = session.beginTransaction();        List list = query.getExecutableCriteria(session)            .list();//子查询List list = session.createCriteria(Student.class)            //下面两种方式都行            .add( Property.forName("name").in(query))//          .add( Subqueries.propertyIn("name" , query))            .list();

SQL查询

通过Query的子接口SQLQuery.它多了两个方法:
addEntity():将查询到的记录与特定的实体相关联。
addScalar():将查询到的记录关联成标量值。

//明确指定查询中返回的数据列返回的类型session.createSQLQeury("select * from student_inf")       .addScalar("name",StandardBasicTypes.STRING)       .list();//实体查询String sql = "select * from enroment_inf where year=?1";List list = session.createSQLQuery(sql)            .addEntity(Enrolment.class)            .setInteger("1",2005)            .list();//映射多个实体String sqlString = "select s.*,e.*,c.* "            + "from student_inf s,enrolment_inf e,course_inf c "            + "where s.student_id = e.student_id "            + "and e.course_code = c.course_code";        List list = session.createSQLQuery(sqlString)            .addEntity("s", Student.class)            .addEntity("e", Enrolment.class)            .addEntity("c", Course.class)            .list();for (Object ele : list)        {            Object[] objs = (Object[])ele;            Student s = (Student)objs[0];            Enrolment e = (Enrolment)objs[1];            Course c = (Course)objs[2];            System.out.println(s.getName() + "\t"                + e.getYear() + "\t" + e.getSemester()                + "\t" + c.getName());        }

还可将查询结果转换成非持久化类。(javabean).Query接口有个setResultTransformer()方法。传进一个Transformers对象即可转换。

String sqlString = "select s.name stuName, c.name courseName "            + "from student_inf s,enrolment_inf e,course_inf c "            + "where s.student_id = e.student_id "            + "and e.course_code = c.course_code ";        List list = session.createSQLQuery(sqlString)            .setResultTransformer(Transformers                .aliasToBean(StudentCourse.class))            .list();//StudnetCourse只有两个属性stuName,courseName。

处理关联和映射

将关联实体转换成查询结果
SQLQuery.addJoin(String alias,String property):第一个是转换后的实体名,第二个是待转换的实体属性。

String sqlString = "select s.* , e.* from student_inf s , "        + "enrolment_inf e where s.student_id=e.student_id";    List list = session.createSQLQuery(sqlString)        .addEntity("s", Student.class)        .addJoin("e" , "s.enrolments")        .list();    for (Object ele : list)    {        Object[] objs = (Object[])ele;        Student s = (Student)objs[0];        Enrolment e = (Enrolment)objs[1];        System.out.println(s.getName() + "\t" + e.getYear());    }

命名sql查询

可以使用@NamedNativeQuery来定义原生sql查询。有如下属性:

name:查询名称
query:sql语句
resultClass:查询映射的实体类的类名
resultSetMapping:指定一个sql结果映射

如果需要使用resultresultSetMapping属性。则需要使用@SqlResultSetMapping定义sql结果映射。
将查询得到的结果转换为标量查询或实体查询。
@SqlResultSetMapping有以下属性:

name:
columns:值为@ColumnResult注解数组。每个@ColumnResult定义一个标量查询。
entities:值为@EntityResult注解数组。每个@EntityResult定义一个实体查询
classes:值为@ConstructorResult注解数组。每个@ConstructorResult负责将指定的多列转换为javabean的对应属性。

@NamedNativeQueries({@NamedNativeQuery(name="simpleQuery"    , query="select s.student_id , s.name from student_inf s"    , resultClass=Student.class),@NamedNativeQuery(name="queryTest"    , query="select s.*,e.*,c.* from student_inf s,enrolment_inf e,"    + " course_inf c where s.student_id = e.student_id and"    + " e.course_code = c.course_code and e.year=:targetYear"    , resultSetMapping = "firstMapping"),@NamedNativeQuery(name="callProcedure"    , query="{call select_all_student()}"    , resultSetMapping = "secondMapping")})@SqlResultSetMappings({@SqlResultSetMapping(name="firstMapping"    , entities={@EntityResult(entityClass=Student.class),        @EntityResult(entityClass=Enrolment.class),        @EntityResult(entityClass=Course.class , fields=        {            @FieldResult(name="courseCode" , column="c.course_code"),            @FieldResult(name="name" , column="c.name")        })    }    , columns={@ColumnResult(name="s.name" , type=String.class)}),@SqlResultSetMapping(name="secondMapping"    , entities={@EntityResult(entityClass=Student.class , fields=        {            @FieldResult(name="studentNumber" , column="student_id"),            @FieldResult(name="name" , column="name")        })    })})//调用List list = session.getNamedQuery("queryTest")            .setInteger("targetYear" , 2005)            .list();for(Object ele : list)        {            Object[] objs = (Object[])ele;            Student s = (Student)objs[0];            Enrolment e = (Enrolment)objs[1];            Course c = (Course)objs[2];            String stuName = (String)objs[3];            System.out.println(s.getName() + "\t"                + e.getYear() + "\t" + e.getSemester()                + "\t=" + e.getCourse().getName() + "=\t" + stuName);        }

调用存储过程

可以通过命名sql查询来调用存储过程或函数。对于函数,该函数必须返回一个结果集。对于存储过程,该存储过程的第一个参数必须是参数,且其数据类型必须是结果集。

//创建一个简单的存储过程create procedure select_all_student()select * from student_inf;//定义一个调用存储过程的命名sql@NameNativeQuery(name="callProcedure"                    ,query="{call select_all_student()}"                    ,resultSetMapping="secondMapping")@SqlResultSetMapping(name="secondMapping"                ,entities={@EntityResult(entityClass=Student.class,fileds=                {                    @FieldResult(name="studentNumber",column="student_id"),                    @FieldResult(name="name",column="name")                })            })//调用存储过程List list = session.getNamedQuery("callProcedure")            .list();

使用定制sql

不使用hibernate提供的默认sql语句来执行。而使用自己写的。
如果想要调用存储过程来执行删除,更新等操作。则需指定callable=true.

@SQLInsert(callable=true,sql="{call createPerson(?,?)}")@SQLUpdate(callable=true,sql="{? = call UpdatePerson(?)}")@SQLDelete(callable=true,sql="{? = call deletePerson(?,?)}")

还可用命名sql实现定制装载。例如在装载News时为title属性的前后增加三个等号。

@SQLInsert(sql="insert into news_inf(content , title) values(upper(?), ?)")@SQLUpdate(sql="update news_inf set content=upper(?), title=? where news_id=?")@SQLDelete(sql="delete from news_inf where news_id=?")@SQLDeleteAll(sql="delete from news_inf")@Loader(namedQuery = "news_loader")@NamedNativeQuery(name="news_loader"    , query="select news_id , concat('===' , concat(title , '===')) as title"        + " , content from news_inf n where news_id=?"    , resultClass = News.class)@Entity@Table(name="news_inf")public class News{    @Id @Column(name="news_id")    @GeneratedValue(strategy=GenerationType.IDENTITY)    private Integer id;    private String title;    private String content;

数据过滤

过滤器与持久化类的@Where注解非常相似。他们的区别是过滤器可以带参数,运用程序可以在运行时决定是否启动指定的过滤器。使用怎样的参数值。而@Where注解将一直生效。且无法动态传入参数。
使用过滤器的三个步骤:
1. 定义过滤器,使用hibernate提供的@FilterDef注解定义过滤器。如果需要定义多个过滤器则需要使用@FilterDefs注解组合多个@FilterDef.
2. 使用@Filter元素应用过滤器
3. 在代码中通过session启用过过滤器。

@FilterDef属性:
name:
defaultCondition:默认过滤条件。可不写。
parameters:指定过滤器中sql表达式支持的参数。

@FilterDefs({@FilterDef(name="effectiveDate"    , parameters={@ParamDef(name="asOfDate" , type="date")}),@FilterDef(name="category"    , parameters={@ParamDef(name="catId" , type="int")})})@Entity@Table(name="product_inf")@Filter(name="effectiveDate"    , condition=":asOfDate BETWEEN eff_start_date AND eff_end_date")public class Product{    @Id @Column(name="product_id")    @GeneratedValue(strategy=GenerationType.IDENTITY)    private Integer id;    @Column(name="product_name")    private String name;    @Column(name="stock_number")    private int stockNumber;    @Column(name="eff_start_date")    private Date effectiveStartDate;    @Column(name="eff_end_date")    private Date effectiveEndDate;    @ManyToMany(targetEntity=Category.class)    @JoinTable(name="product_category"        , joinColumns=@JoinColumn(name="product_id")        , inverseJoinColumns=@JoinColumn(name="category_id"))    @Filters({        @Filter(name="effectiveDate"            , condition=":asOfDate BETWEEN eff_start_date and eff_end_date"),        @Filter(name="category"            , condition="category_id = :catId")    })    private Set<Category> categories        = new HashSet<>();

系统默认不启动过滤器,必须通过session.enableFilteer(String filterName)才可以启用,而且一旦启动他将在整个session内有效。知道session调用disableFilter()方法。

session.enableFilter("effectiveDate")            .setParameter("asOfDate", new Date());        session.enableFilter("category")            .setParameter("catId", 2);        List list = session.createQuery("from Product as p").list();        for (Object obj : list)        {            Product p = (Product)obj;            System.out.println(p.getName());            System.out.println("----" + p.getCategories());         }

拦截器

通过Interceptor接口,可以从session中回调应用程序的特定方法。这种回调机制可以让应用程序在持久化对象被保存、删除、修改、加载之前,检查并修改其属性。
使用步骤:
1. 定义实现Interceptor接口的拦截器。最好继承EmptyInterceptor类。

public class MyInterceptor extends EmptyInterceptor{    private int updates;    private int creates;    public void onDelete(Object entity , Serializable id ,        Object[] state , String[] propertyNames , Type[] types)    {        // do nothing    }    public boolean onFlushDirty(Object entity , Serializable id ,        Object[] currentState, Object[] previousState,        String[] propertyNames, Type[] types)    {        updates++;        for ( int i = 0; i < propertyNames.length; i++ )        {            if ( "lastUpdateTimestamp".equals( propertyNames[i] ) )            {                currentState[i] = new Date();                return true;            }        }        return false;    }    public boolean onLoad(Object entity , Serializable id ,        Object[] state,String[] propertyNames,Type[] types)    {        for ( int i = 0; i < propertyNames.length ; i++ )        {            if ( "name".equals( propertyNames[i] ) )            {                System.out.println( state[i] );                return true;            }        }        return false;    }    public boolean onSave(Object entity , Serializable id ,        Object[] state,String[] propertyNames,Type[] types)    {        creates++;        for ( int i = 0; i < propertyNames.length; i++ )        {            if ("createTimestamp".equals( propertyNames[i]))            {                state[i] = new Date();                return true;            }        }        return false;    }    public void postFlush(Iterator entities)    {        System.out.println("´创建的次数"            + creates + ", 更新的次数" + updates);    }    public void preFlush(Iterator entities)    {        // do nothing    }    public void beforeTransactionCompletion(Transaction tx)    {        System.out.println("事务即将开始");    }    public void afterTransactionCompletion(Transaction tx)    {        System.out.println("事务已将结束");    }
  1. 通过sessionFactory.openSession(Interceptor in)启动局部拦截器。或Configuration.setInterceptor(Interceptor in)来设置全局拦截器。
static Configuration cfg = new Configuration()        .configure()        .setInterceptor(new MyInterceptor());

事件系统

可作为拦截器的补充或代替拦截器。
Session接口的每个方法都有对应的事件。比如LoadEvent,FlushEvent等。当session调用某个方法时,session会生成对应的事件。并激活对应的事件监听器。
可以在系统中实现并注册LoadEventListener监听器,负责所有的调用session.load()方法的请求。
使用事件系统的步骤:
1. 实现自己的事件监听器类。一般采用继承系统默认的监听器,扩展特定的方法。
2. 注册自定义事件监听器,代替系统默认的事件监听器。

public class MySaveListener extends DefaultSaveEventListener{    public Serializable performSaveOrUpdate(SaveOrUpdateEvent event)    {        System.out.println(event.getObject());        return super.performSaveOrUpdate(event);    }}public class MyLoadListener extends DefaultLoadEventListener{    public void onLoad(LoadEvent event,        LoadEventListener.LoadType loadType)        throws HibernateException    {        System.out.println(event.getEntityClassName()            + "==========" + event.getEntityId());        super.onLoad(event, loadType);    }}

hibernate提供了一个EventListenerRegistry接口来注册事件监听器。一共有三个方法:
appendListener();加到系统默认的事件监听器后面
prependListener():加到系统默认的事件监听器前面
setListener():替换系统默认的事件监听器

static ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()        .applySettings(cfg.getProperties()).build();    static SessionFactory sf = cfg.buildSessionFactory(serviceRegistry);    static{        EventListenerRegistry elr = ((SessionFactoryImpl)sf)            .getServiceRegistry().getService(EventListenerRegistry.class);        elr.setListeners(EventType.SAVE, MySaveListener.class);        elr.setListeners(EventType.LOAD, MyLoadListener.class);    }
0 0
原创粉丝点击