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"/>
  • 相关阅读:
    2. Add Two Numbers
    1. Two Sum
    22. Generate Parentheses (backTracking)
    21. Merge Two Sorted Lists
    20. Valid Parentheses (Stack)
    19. Remove Nth Node From End of List
    18. 4Sum (通用算法 nSum)
    17. Letter Combinations of a Phone Number (backtracking)
    LeetCode SQL: Combine Two Tables
    LeetCode SQL:Employees Earning More Than Their Managers
  • 原文地址:https://www.cnblogs.com/namelessmyth/p/10910466.html
Copyright © 2011-2022 走看看