hibernate高级应用

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值