zoukankan      html  css  js  c++  java
  • Hibernate基础

    一.注解配置

    1.Column的属性

    @Target({METHOD, FIELD})
    @Retention(RUNTIME)
    public @interface Column {
    
        /**
         * (Optional) The name of the column. Defaults to
         * the property or field name.
         */
        String name() default "";
    
        /**
         * (Optional) Whether the column is a unique key.  This is a
         * shortcut for the <code>UniqueConstraint</code> annotation at the table
         * level and is useful for when the unique key constraint
         * corresponds to only a single column. This constraint applies
         * in addition to any constraint entailed by primary key mapping and
         * to constraints specified at the table level.
         */
        boolean unique() default false;
    
        /**
         * (Optional) Whether the database column is nullable.
         */
        boolean nullable() default true;
    
        /**
         * (Optional) Whether the column is included in SQL INSERT
         * statements generated by the persistence provider.
         */
        boolean insertable() default true;
    
        /**
         * (Optional) Whether the column is included in SQL UPDATE
         * statements generated by the persistence provider.
         */
        boolean updatable() default true;
    
        /**
         * (Optional) The SQL fragment that is used when
         * generating the DDL for the column.
         * <p> Defaults to the generated SQL to create a
         * column of the inferred type.
         */
        String columnDefinition() default "";
    
        /**
         * (Optional) The name of the table that contains the column.
         * If absent the column is assumed to be in the primary table.
         */
        String table() default "";
    
        /**
         * (Optional) The column length. (Applies only if a
         * string-valued column is used.)
         */
        int length() default 255;
    
        /**
         * (Optional) The precision for a decimal (exact numeric)
         * column. (Applies only if a decimal column is used.)
         * Value must be set by developer if used when generating
         * the DDL for the column.
         */
        int precision() default 0;
    
        /**
         * (Optional) The scale for a decimal (exact numeric) column.
         * (Applies only if a decimal column is used.)
         */
        int scale() default 0;
    }

    (1) name 可选,列名(默认值是属性名)

    (2) unique 可选,是否在该列上设置唯一约束(默认值false)
    (3) nullable 可选,是否设置该列的值可以为空(默认值false)
    (4) insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true)
    (5) updatable 可选,该列是否作为生成的update语句中的一个列(默认值true)
    (6) columnDefinition 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植)
    (7) table 可选,定义对应的表(默认为主表)
    (8) length 可选,列长度(默认值255)
    (8) precision 可选,列十进制精度(decimal precision)(默认值0)
    (10) scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)

    a.columnDefinition属性

    column注解中的columnDefinition属性用于覆盖数据库DDL中的语句:(MySql)

        @Column(columnDefinition = "int(11) DEFAULT NULL COMMENT '类型'")
        public Integer getType() {
            return type;
        }

    因此,又可以为该列添加comment注释。

    然而,columnDefinition不推荐使用,因为可能导致移植性不好,各个数据库不兼容等。

    b.如何设置列默认值?

    @Column(name="C_NAME",length=40)
        private String name="nnnn";

    直接在bean中赋值即可。

    c.oracle中char 和byte如何选择?

    char(n) = char(n byte) 按字节存储
    char(n char) 根据字符集不同有所不同,按字存储;

    varchar2最大长度为4000。

    d.char,varchar,varchar2区别

      1.CHAR的长度是固定的,而VARCHAR2的长度是可以变化的, 比如,存储字符串“abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符),而同样的VARCHAR2 (20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时,按实际长度存储。  
      2.CHAR的效率比VARCHAR2的效率稍高。  
      3.目前VARCHAR是VARCHAR2的同义词。工业标准的VARCHAR类型可以存储空字符串,但是oracle不这样做,尽管它保留以后这样做的权利。Oracle自己开发了一个数据类型VARCHAR2,这个类型不是一个标准的VARCHAR,它将在数据库中varchar列可以存储空字符串的特性改为存储NULL值。如果你想有向后兼容的能力,Oracle建议使用VARCHAR2而不是VARCHAR。 

      4.VARCHAR2虽然比CHAR节省空间,但是如果一个VARCHAR2列经常被修改,而且每次被修改的数据的长度不同,这会引起‘行迁移’(Row Migration)现象,而这造成多余的I/O,是数据库设计和调整中要尽力避免的,在这种情况下用CHAR代替VARCHAR2会更好一些。 

    e.precision与scale

    NUMBER[(precision[,scale])] 存储零,正数和负数。
    precision 是总共的数字位数,默认是38位十进制数——最大的数。
    scale是小数点右边的数,默认是零。

    比如:
    pay NUMBER 和pay NUMBER (38,0)意思一样。
    一个正数的scale告诉数据库,小数点右边结束的位数。scale的合法范围是-84~127.
    scale为负数的意思是说从小数点前面的第几个数开始四舍五入。

    要重新设置精度,必须清空该列。

    precision的精度范围是指经过四舍五入后的精度。比如定义number(3,1),则12.3456 ->12.3不会报错;而123.4则报错,因为四舍五入后为123.4超过precision范围。

    Integer 定义Number(10,0)

    Long的话直接定义成Number(38,0)即可。

    f.Oracle中Date和Timestamp区别

    DATE数据类型的主要问题是它粒度不能足够区别出两个事件哪个先发生。ORACLE已经在DATE数据类型上扩展出来了TIMESTAMP数据类型,它包括了所有DATE数据类型的年月日时分秒的信息,而且包括了小数秒的信息

    g.其他

    Clob,Blob

    CLOB使用CHAR来保存数据。 如:保存XML文档。  
    BLOB就是使用二进制保存数据。 如:保存位图。

    2.注解定义Bean

    参考:http://hi.baidu.com/wjx_5893/item/7b773aab2acbf217a9cfb7fe

    http://blog.163.com/lidan_grace/blog/static/5669483720113285648242/

    1、@Entity(name="EntityName")
    必须,name为可选,对应数据库中一的个表

    2、@Table(name="",catalog="",schema="")
    可选,通常和@Entity配合使用,只能标注在实体的class定义处,表示实体对应的数据库表的信息
    name:可选,表示表的名称.默认地,表名和实体名称一致,只有在不一致的情况下才需要指定表名
    catalog:可选,表示Catalog名称,默认为Catalog("").
    schema:可选,表示Schema名称,默认为Schema("").

    注:一个数据库通常包含多个catalog,一个catalog包含多个schema

    3.主键设置

    @id定义了映射到数据库表的主键的属性.

    详细的主键生成策略见http://hi.baidu.com/wjx_5893/item/7b773aab2acbf217a9cfb7fe

    4.@Transient

    可选
    @Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性.
    如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic
    示例:
    //根据birth计算出age属性
    @Transient
    public int getAge() {
    return getYear(new Date()) - getYear(birth);
    }

    5.@Basic(fetch=FetchType,optional=true)
    可选
    @Basic 表示一个简单的属性到数据库表的字段的映射 , 对于没有任何标注的 getXxxx() 方法 , 默认即为 @Basic
    fetch: 表示该属性的读取策略 , 有 EAGER 和 LAZY 两种 , 分别表示主支抓取和延迟加载 , 默认为 EAGER.
    optional: 表示该属性是否允许为 null, 默认为 true
    示例 :
    @Basic(optional=false)
    public String getAddress() {
    return address;
    }

    3.关联映射

    关联关系映射通常情况是最难配置正确的。在这个部分中,我们从单向关系映射开始,然后考虑双向关系映射,逐步讲解典型的案例。

    a.@ManyToOne

    表示一个多对一的映射,该注解标注的属性通常是数据库表的外键
    optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true (optional表示该字段是否是可选的)
    fetch:表示抓取策略,默认为FetchType.EAGER (FetchType.LAZY,FetchType.EAGER)
    cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作多个属性用cascade={CascadeType.MERGE,CascadeType.REFRESH}来写

    targetEntity:表示该属性关联的实体类型.该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity.

    b. Cascade详解

    CascadeType.MERGE级联更新:若items属性修改了那么order对象保存时同时修改items里的对象。对应EntityManager的merge方法
    CascadeType.REFRESH级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应EntityManager的refresh(object)方法有效。即会重新查询数据库里的最新数据
    CascadeType.PERSIST级联保存:对order对象保存时也对items里的对象也会保存。对应EntityManager的presist方法
    CascadeType.REMOVE级联删除:对order对象删除也对items里的对象也会删除。对应EntityManager的remove方法
    CascadeType.PERSIST只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)
    CascadeType.MERGE指A类新增或者变化,会级联B对象(新增或者变化)
    CascadeType.REMOVE只有A类删除时,会级联删除B类;
    CascadeType.ALL包含所有;
    CascadeType.REFRESH没用过。
    综上:大多数情况用CascadeType.MERGE就能达到级联跟新又不报错,用CascadeType.ALL时要斟酌下CascadeType.REMOVE

    c.@JoinColumn

    @JoinColumn和@Column类似,介量描述的不是一个简单字段,而一一个关联字段,例如.描述一个@ManyToOne的字段.
    name:该字段的名称.由于@JoinColumn描述的是一个关联字段,如ManyToOne,则默认的名称由其关联的实体决定.

    d.@OneToMany

    描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段. 
    fetch:表示抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库预先读取到内存 
    cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新删除 
    例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除 

    e.mappedBy

    a) 只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性; 
    b) mappedBy标签一定是定义在the owned side(被拥有方的),他指向the owning side(拥有方);
    c) mappedBy的含义,应该理解为,拥有方能够自动维护 跟被拥有方的关系; 
       当然,如果从被拥有方,通过手工强行来维护拥有方的关系也是可以做到的。

    d) mappedBy跟JoinColumn/JoinTable总是处于互斥的一方,可以理解为正是由于拥有方的关联被拥有方的字段存在,拥有方才拥有了被 拥有方。mappedBy这方定义的JoinColumn/JoinTable总是失效的,不会建立对应的字段或者表

    f.@JoinTable

    2.1单向关联多对一

    @Entity
    @Table(name="T_ACCOUNT")
    public class Account  implements java.io.Serializable {
       
        @Id    
        @Column(name="C_ID",length=40)    
        @GeneratedValue(generator="idGenerator")                                               
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=160,nullable=false)  
        private String name;
    
    }
    @Entity
    @Table(name="T_MESSAGE" )
    public class Message  implements java.io.Serializable {
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @ManyToOne
        @JoinColumn(name="C_ACCOUNT_ID")  
        private Account account;
    }

    一个account拥有多个message

    2.2单向关联一对一

    @Entity
    @Table(name="T_ACCOUNT")
    public class Account  implements java.io.Serializable {
       
        @Id    
        @Column(name="C_ID",length=40)    
        @GeneratedValue(generator="idGenerator")                                               
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=160,nullable=false)  
        private String name;

        @OneToOne(cascade = CascadeType.ALL)
        @JoinColumn(name="C_PERSON_ID")
        private Person person; }
    @Entity
    @Table(name="T_PERSON" )
    public class Person  implements java.io.Serializable {
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=40)
        private String name;
    
        @OneToOne(mappedBy = "person")
        private Account account;
    }

    2.3双向关联多对一

    @Entity
    @Table(name="T_ACCOUNT")
    public class Account  implements java.io.Serializable {
       
        @Id    
        @Column(name="C_ID",length=40)    
        @GeneratedValue(generator="idGenerator")                                               
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=160,nullable=false)  
        private String name;
      
    @OneToMany(cascade = { CascadeType.ALL }, mappedBy = "account")
    private Set<Message> messages;
    }
    @Entity
    @Table(name="T_MESSAGE" )
    public class Message  implements java.io.Serializable {
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @ManyToOne
        @JoinColumn(name="C_ACCOUNT_ID")  
        private Account account;
    }

    一个account拥有多个message

    2.4自关联

    @Entity
    @Table(name="T_CATEGORY")
    public class Category  implements java.io.Serializable {
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=200)  
        private String name;
        
        @ManyToOne
        @JoinColumn(name = "C_PARENT_ID")  
        private Category parent;
        
        @OneToMany(cascade = { CascadeType.ALL }, mappedBy = "parent")  
        @OrderBy("weight") 
        private Set<Category> children;
    }

    2.5双向多对多关联(有连接表)

    @Entity
    @Table(name="T_ROLE" )
    public class Role  implements java.io.Serializable {
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=100)  
        private String name;
    
        @ManyToMany(targetEntity=Right.class)  
        @JoinTable(name="T_ROLE_RIGHT",  
            joinColumns={@JoinColumn(name="C_ROLE_ID")},   
            inverseJoinColumns={@JoinColumn(name="C_RIGHT_ID")}) 
        private Set<Right> rights;
    }
    @Entity
    @Table(name="T_RIGHT" )
    public class Right  implements java.io.Serializable {
    
    
        @Id    
        @Column(name="C_ID",length=40)  
        @GeneratedValue(generator="idGenerator")
        @GenericGenerator(name="idGenerator", strategy="uuid")
        private String id;
        
        @Column(name="C_NAME",length=100)  
        private String name;
        
        @ManyToMany(targetEntity=Role.class, mappedBy="rights")
        private Set<Role> roles;
    }

    4.对象生命周期

     

     4.1get()和load()区别

    1.从返回结果上对比:
    load方式检索不到的话会抛出org.hibernate.ObjectNotFoundException异常
    get方法检索不到的话会返回null

    2.从检索执行机制上对比: get方法和find方法都是直接从数据库中检索 而load方法的执行则比较复杂首先查找session的persistent Context中是否有缓存,如果有则直接返回 如果没有则判断是否是lazy,如果不是直接访问数据库检索,查到记录返回,查不到抛出异常 如果是lazy则需要建立代理对象,对象的initialized属性为false,target属性为null 在访问获得的代理对象的属性时,检索数据库,如果找到记录则把该记录的对象复制到代理对象的target上,并将initialized=true,如果找不到就抛出异常。

    3.根本区别说明
    如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来 延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常。所说的load方法抛异常是指在使用 该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时(注意:这就是由于“延迟加载”在作怪)。

    由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。

    对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。

    对于load和get方法返回类型:虽然好多书中都这么说:“get()永远只;实体类”,但实际上这是不正确的,get方法如果在 session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是 原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是 返回的还是代理对象,只不过已经加载了实体数据。

    get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。

    4.简单总结

    总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。

    4.2merge

    merge的作用是:新new一个对象,如果该对象设置了ID,则这个对象就当作游离态处理: 当ID在数据库中不能找到时,用update的话肯定会报异常(org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session : ),然而用merge的话,就会insert。 当ID在数据库中能找到的时候,update与merge的执行效果都是更新数据,发出update语句。

    用merge的话,它会把第一个的对象数据赋值给已经处于持久化的那个对象中,自己本身不得变为持久态;

     4.3flush

    该方法进行清理缓存的操作,执行一系列的SQL语句,但不提交事务。由此可见该方法提前将SQL缓冲区的SQL语句发送到数据库中。另外执行flush方法时Hibernate会对当前所操作的对象加锁,且该锁为行级别锁,防止其他事务对当前对象脏读。

     4.4evict

    清除指定的缓存对象。session.evict(ohj)

    4.5clear

     该方法会强制清空当前session中的缓存 。

    5.事务控制

     5.1Spring事务的传播行为 

    在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。 

    Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked 

    如果遇到checked意外就不回滚。 

    如何改变默认规则: 

    1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

    2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

    3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
    在整个方法运行前就不会开启事务 .

     还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。 

     propagation.属性含义:

      REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。 

           NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。 

           REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。 

           MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。 

           SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。 

           NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。 

           NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。 

    6.Session

    详细:http://aixiangct.blog.163.com/blog/static/9152246120113652732924/

  • 相关阅读:
    SQL Server创建表
    SQL Server创建数据库
    SQL Server创建索引
    SQL Server创建视图
    SQL Server创建存储过程
    SQL Server创建触发器
    Unity3D与VS2008结合,加快Unity3D C#开发!
    c#哈希表的用法
    长沙做网站公司解密如何编写高效率的SQL语句
    高效SQL语句必杀技
  • 原文地址:https://www.cnblogs.com/jintianfan/p/3491268.html
Copyright © 2011-2022 走看看