zoukankan      html  css  js  c++  java
  • Hibernate笔记

    开始

      好记心不如烂笔头,很多技术如果长时间容易遗忘,故特用此文记录下一些Hibernate的使用技巧。希望将来的自己和所有看到这篇文章的童鞋都能从中获益。

    Hibernate的使用场景。

    在一些对性能要求不是很高,但对开发速度有要求的小型项目中,Hibernate能显著降低代码量和提高增删改业务的开发效率。


     

    • 对象映射关系-基础配置。

      拿部门,员工这种典型一对多关系来做例子。

      首先是公共父类,可以定义一些所有表都需要用到的公共字段,还有主键生成规则。

      ID:默认所有表的逻辑主键,需要用@GeneratedValue定义值的生成策略。generator里面指定的是一个生成器Key,在子类中可以通过这个Key关联,然后再定义每个表的具体生成策略。

     1 @MappedSuperclass
     2 @AccessType(Type.FIELD)
     3 public class PublicPO extends PublicObj {
     4 
     5     /** id 逻辑主键 */
     6     @Id
     7     @GeneratedValue(strategy = GenerationType.AUTO, generator = "caGenerator")
     8     @Column(name = "id")
     9     private Long id;
    10 
    11     /** 创建人 */
    12     @Column(name = "CREATE_BY", updatable = false)
    13     private String createBy;
    14 
    15     /** 创建时间 */
    16     @Column(name = "CREATE_TIME", updatable = false)
    17     private Date createTime;
    18 
    19     /** 更新人 */
    20     @Column(name = "UPDATE_BY")
    21     private String updateBy;
    22 
    23     /** 更新时间 */
    24     @Column(name = "UPDATE_TIME")
    25     private Date updateTime;
    26 
    27     public Long getId() {
    28         return id;
    29     }
    30 
    31     public void setId(Long id) {
    32         this.id = id;
    33     }
    34 
    35     public String getCreateBy() {
    36         return createBy;
    37     }
    38 
    39     public void setCreateBy(String createBy) {
    40         this.createBy = createBy;
    41     }
    42 
    43     public String getUpdateBy() {
    44         return updateBy;
    45     }
    46 
    47     public void setUpdateBy(String updateBy) {
    48         this.updateBy = updateBy;
    49     }
    50 
    51     public Date getCreateTime() {
    52         return createTime;
    53     }
    54 
    55     public void setCreateTime(Date createTime) {
    56         this.createTime = createTime;
    57     }
    58 
    59     public Date getUpdateTime() {
    60         return updateTime;
    61     }
    62 
    63     public void setUpdateTime(Date updateTime) {
    64         this.updateTime = updateTime;
    65     }
    66 
    67 }

    部门类(Department)继承公共父类,同时定义的主键生成策略为序列,指定序列名。

    部门和员工为一对多关系,所以部门中有一个List<Employee>属性,mappedBy = "department" 意思是让employee来维护关联关系;orphanRemoval = true 意思是自动删除不再和部门关联的员工。

    由于部门之间还有父子关系,所以部门中还会有一个Department类型的属性,为多对一自连接关系。代码如下:

     1 @Entity
     2 @Table(name = "s_department")
     3 @SequenceGenerator(name = "caGenerator", sequenceName = "seq_department", allocationSize = 1)
     4 public class Department extends PublicPO {
     5 
     6     @Column(name = "NAME")
     7     private String name;
     8 
     9     @Column(name = "TYPE")
    10     private String type;
    11 
    12     @Column(name = "STRATEGY")
    13     private String strategy;
    14 
    15     @Column(name = "status")
    16     private Integer status;
    17 
    18     @ManyToOne(fetch = FetchType.LAZY)
    19     @JoinColumn(name = "PARENT_ID")
    20     private Department parent;
    21     
    22     @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, mappedBy = "department", orphanRemoval = true)
    23     private List<Employee> employeeList;
    24 
    25     public String getName() {
    26         return name;
    27     }
    28 
    29     public void setName(String name) {
    30         this.name = name;
    31     }
    32 
    33     public String getType() {
    34         return type;
    35     }
    36 
    37     public void setType(String type) {
    38         this.type = type;
    39     }
    40 
    41     public String getStrategy() {
    42         return strategy;
    43     }
    44 
    45     public void setStrategy(String strategy) {
    46         this.strategy = strategy;
    47     }
    48 
    49     public Integer getStatus() {
    50         return status;
    51     }
    52 
    53     public void setStatus(Integer status) {
    54         this.status = status;
    55     }
    56 
    57     public Department getParent() {
    58         return parent;
    59     }
    60 
    61     public void setParent(Department parent) {
    62         this.parent = parent;
    63     }
    64 
    65     public List<Employee> getEmployeeList() {
    66         return employeeList;
    67     }
    68 
    69     public void setEmployeeList(List<Employee> employeeList) {
    70         this.employeeList = employeeList;
    71     }
    72 }

    员工类

     1 @Entity
     2 @Table(name = "s_employee")
     3 @SequenceGenerator(name = "caGenerator", sequenceName = "seq_employee", allocationSize = 1)
     4 public class Employee extends PublicPO{
     5 
     6     @Column(name = "NAME")
     7     private String name;
     8 
     9     @Column(name = "DESC")
    10     private String desc;
    11     
    12     @Column(name = "AGE")
    13     private Integer age;
    14 
    15     @Column(name = "hire_date")
    16     private Date hireDate;
    17 
    18     @ManyToOne(fetch = FetchType.LAZY)
    19     @JoinColumn(name = "DEPARTMENT_ID")
    20     private Department department;
    21 
    22     public String getName() {
    23         return name;
    24     }
    25 
    26     public void setName(String name) {
    27         this.name = name;
    28     }
    29 
    30     public String getDesc() {
    31         return desc;
    32     }
    33 
    34     public void setDesc(String desc) {
    35         this.desc = desc;
    36     }
    37 
    38     public Integer getAge() {
    39         return age;
    40     }
    41 
    42     public void setAge(Integer age) {
    43         this.age = age;
    44     }
    45 
    46     public Date getHireDate() {
    47         return hireDate;
    48     }
    49 
    50     public void setHireDate(Date hireDate) {
    51         this.hireDate = hireDate;
    52     }
    53 
    54     public Department getDepartment() {
    55         return department;
    56     }
    57 
    58     public void setDepartment(Department department) {
    59         this.department = department;
    60     }
    61 }

     部门下新增员工和修改员工的设计思路。

    首先有一个前提,就是前端页面每次都会把一个部门下的所有员工全部传给后台。后台处理提供2种思路

    1.先删后增。先将部门和部门下的所有员工信息全部删除,然后再把前台传过来的数据存入数据库。

    方案优点:不需要人工判断,后台传入的ID是否在数据库存在,存在就修改,不存在则新增。

    方案缺点:如果部门和员工信息关联的表比较多,那从前台传过来的信息就会很多,而且将来如果多一个关联对象,可能还要修改代码。

    2.让hibernate自己去判断修改还是新增。在调用对象的SAVE方法时,PO要从前台VO直接转而不要通过JPA的查询方法查出。如果需要校验可以放在另一个事务里或者使用MyBatis。

    方案优点:不需要人工判断,代码量少。

    方案缺点:对于程序猿要求比较高,需要他比较熟悉Hibernate底层的处理机制。

    注意点:

    如果数据库中1个部门存在5个员工,而调用save时list中只有4个,那当orphanRemoval = true时,Hibernate是会自动删除那个缺失的员工的。

    对于员工从一个部门调到另一个部门的情况,即某员工A的部门ID从1改成了2.这种需要先将该员工的id设置为空,然后找到新部门的部门ID。否则Hibernate发现原来部门的员工少了一个会自动删除这个员工。

    3.人工判断数据是否存在,存在则修改,不存在则新增.先根据ID判断是新增还是修改.先判断主表在判断子表.

    方案优点:逻辑全靠程序猿自己控制,不怎么依赖Hibernate,定制性比较强.

    方案缺点:判断逻辑比较多,比较依赖程序猿的细致程度。


    CascadeType中几个值的具体含义

    CascadeType.REFRESH:级联刷新,当多个用户同时作操作一个实体,为了用户取到的数据是最新的实时的,在使用实体中的数据之前就可以调用一下refresh()方法!

    CascadeType.REMOVE:级联删除,当调用部门的remove()方法时,会删除部门List中的所有员工数据!

    CascadeType.MERGE:级联更新,当调用了Merge()方法时,如果部门中的数据改变了会相应的更新员工信息中的数据。

    CascadeType.PERSIST:级联保存,当调用了部门的Persist()方法,会级联保存相应List中的员工数据

    CascadeType.ALL:包含以上所有级联属性。

    (注:以上几种级联操作,只能实在满足数据库的约束时才能生效,比如上边的部门和员工存在主外键关联,那执行REMOVE()方法时可能不一定能级联删除)


    Hibernate怎么实现仅仅查询自己想要的字段?

    • 使用高级查询DetachedCriteria实现,代碼如下:
    String alias = "user_"; //查詢時的table別名  
    DetachedCriteria dc = DetachedCriteria.forClass(User.class,alias);  
    ProjectionList pList = Projections.projectionList();  
    pList.add(Projections.property(alias + "." + "id").as("id"));  
    pList.add(Projections.property(alias + "." + "name").as("name"));  
    pList.add(Projections.property(alias + "." + "age").as("age"));  
    pList.add(Projections.property(alias + "." + "sex").as("sex"));  
    dc.setProjection(pList);  
    dc.setResultTransformer(Transformers.aliasToBean(User.class));  
    resultList = memberService.findByDetached(dc).size();  
    • 通过HQL语句new POJO()实现,代码如下:
    public class Link {
    
        private String id;  
        private String name;  
        private String url;  
        private Integer index;  
    
        public Link(){}
    
        //因为:String hql = "select new Link(id,name) from Link";  
        //所以必须要有接受2个参数的构造函数  
        public Link(String id,String name){  
            this.id = id;  
            this.name = name;  
        }
    
        public String getName() {  
            return name;  
        }
    
        public void setName(String name) {  
            this.name = name;  
        }
    
        public String getUrl() {  
            return url;  
        }
    
        public void setUrl(String url) {  
            this.url = url;  
        }
    } 

    然后使用HQL时,使用这个对象

    String hql = "select new Link(id,name) from Link";
    Query query = session.createQuery(hql);
    //默认查询出来的list里存放的是一个Object对象,但是在这里list里存放的不再是默认的Object对象了,而是Link对象了
    List<Link> links = query.list();
    for(Link link : links){
        String id = link.getId();
        String name = link.getName();
        System.out.println(id + " : " + name);
    }

    第三种方式是在HQL语句中直接写字段名,类似SQL,代码如下:

    String hql = "select id,name from Link";
    Query query = session.createQuery(hql);
    //默认查询出来的list里存放的是一个Object数组,还需要转换成对应的javaBean。
    List<Object[]> links = query.list();
    for(Object[] link : links){
        String id = link[0];
        String name = link[1];
        System.out.println(id + " : " + name);
    }

    常见问题处理:

    • Hibernate的HQL和SQL混用时为什么查询不到SQL的更新结果?

    如果sql在hql的后面执行,那在执行sql查询之前一定要调用flush方法,否则在SQL中无法获取HQL的执行结果

    HQL在SQL后面执行,那必须先调用Refresh方法。否则HQL查询不到SQL的更新结果。

    • 对于Hibernate的flush和refresh方法的使用说明.

    1)这两个方法都是EntityManager类的,但flush方法没参数,refresh方法必须传入一个对象。

    2)Flush主要是将Hibernate缓存中的数据同步到数据库。如果先用了HQL更新了数据,那调用SQL前最好调用这个方法将数据同步到数据库。否则SQL就会查询不到HQL的更新结果。

    3)Refresh是将游离态的对象重新变为持久态,同时也会将数据库中某张表的数据同步到指定的对象上。如果用SQL更新了数据,那用HQL查询前最好调用一下这个方法。

    • 在saveOrUpdate带子表的对象时,子表中的记录翻倍,是怎么回事?

    这种情况一般发生在两种场景下,

    第一种情况。该对象已经有子表数据,然后在此基础上新增。对应到代码中,就是先把list属性get出来,然后add进去多个子表记录,然后saveOrUpdate主表记录。此时新增的子表数据会出现双倍的情况。解决方案:save之前flush一下;

    第二种情况,在代码中首先saveOrUpdate一下一个带有子表记录的对象。然后在调用update方法更新一些字段,此时子表的记录也会变成双倍。解决方案:每次对数据库操作后都flush一下,然后再进行第二次操作。

    • 调用remove方法时,报错EntityNotFoundException: deleted entity passed to persist?

    答:一对多的关系中,如果删除多的一方时,需要先断开多合一的连接,在进行删除。举例来说department和employee是一对多关系,当删除employee时,可以先employee.getDepartment().getEmployees().remove(employee),然后调用remove方法把employee删除.

    • Hibernate如何配置打印SQL语句参数.

    1、配置Spring数据库配置文件,例如:spring-jpa.xml:
    <prop key="hibernate.show_sql">true</prop>--强制打印sql
    <prop key="hibernate.format_sql">true</prop>--格式化sql
    <prop key="hibernate.use_sql_comments">true</prop>--添加sql的注释,说明sql的触发来源
    2、配置log4j或者logback:

    <logger name="org.hibernate.SQL" level="DEBUG" />
    <logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
    <logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
    <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
    <logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="TRACE"/>
  • 相关阅读:
    线段树模板(HDU 6356 Glad You Came)
    Treap模板
    Codeforces Round #499 (Div. 2) D. Rocket题解
    Codeforces Round #499 (Div. 2) C Fly题解
    KMP与AC自动机模板
    HDU 6351 Naive Operations(线段树)
    python核心编程第六章练习6-13
    python核心编程第六章练习6-12
    [转]我为什么要学习python
    python核心编程第六章练习6-11
  • 原文地址:https://www.cnblogs.com/namelessmyth/p/10910466.html
Copyright © 2011-2022 走看看