美文网首页
java web实战学习第一天

java web实战学习第一天

作者: winter_coder | 来源:发表于2018-02-02 14:03 被阅读0次

    搭建maven项目

    具体步骤参照 https://www.jianshu.com/p/49c01353a869 中的说明完成

    搭建spring hibernate 环境

    具体步骤参照 https://www.jianshu.com/p/788733d003a0 中的说明完成
    文档中的依赖包,那么多,并没有一次都添加,只添加最基本的,以后用到哪个,加哪个。

    项目目录建立

    image.png

    创建第一个controller,第一个action。hello world!能够正常显示,说明配置成功。

    数据库设计,建立数据实体类

    数据库表是用其他项目的,实体类是自己创建的,共增加了11个实体类,这里涉及到多个“注解”标签的使用,还有bibernate表关系,一对一,一对多多对一双向关联。虽然没有其他的关联模式,但是下边对都整理出来。

    实体类中接触到的标签:

    @Entity//声明当前类为hibernate映射到数据库的实体类

      *  @Entity(name="tableName") - 必须,注解将一个类声明为一个实体bean。 
      *  属性: name - 可选,对应数据库中的一个表。若表名与实体类名相同,则可以省略。 
    

    @Table(name="User")//为实体Bean指定对应的数据表

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

    @Id //声明当前字段为主键
    @GeneratedValue(strategy = GenerationType.AUTO,generator="")//注解来定义生成主键的策略

     *  Strategy - 表示主键生成策略,取值有:
       *  GenerationType.TABLES 当前主键的值单独保存到一个数据库的表中,结合@TableGenerator使用
       *  GenerationType.SEQUENCE  利用底层数据库提供的序列生成标识符
       *  GenerationType.IDENTITY 根据数据库的Identity字段生成,采取数据库的自增策略
       *  GenerationType.AUTO 根据不同数据库自动选择合适的id生成方案,这里使用mysql,为递增型
     *generator - 表示主键生成器的名称,这个属性通常和ORM框架相关,如:Hibernate 可以指定 uuid 等主键生成方式
    

    @Column(table = "User", columnDefinition="varchar",length = 255, nullable = true)

    可将属性映射到列,使用该注解来覆盖默认值,@Column描述了数据库表中该字段的详细定义,
    这对于根据 JPA 注解生成数据库表结构的工具非常有作用。
    
      name - 可选,表示数据库表中该字段的名称,默认情形属性名称一致 
      nullable - 可选,表示该字段是否允许为 null,默认为 true 
      unique - 可选,表示该字段是否是唯一标识,默认为 false  
      length - 可选,表示该字段的大小,仅对 String 类型的字段有效,默认值255. 
      insertable - 可选,表示在ORM框架执行插入操作时,该字段是否应出现INSETRT语句中,默认为 true
      updateable - 可选,表示在ORM 框架执行更新操作时,该字段是否应该出现在 UPDATE 语句中,默认为 true. 对于一经创建就不可以更改的字段,该属性非常有用,如对于 birthday字段。 
      columnDefinition - 可选,表示该字段在数据库中的实际类型。通常ORM 框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是 DATE,TIME还是TIMESTAMP. 此外,String 的默认映射类型为 VARCHAR, 如果要将 String 类型映射到特定数据库的 BLOB或 TEXT 字段类型,该属性非常有用。
    

    @Version //注解用于支持乐观锁版本控制,通过这种方式可添加对乐观锁定的支持
    @Email(message = "邮箱格式错误")
    @Basic(fetch = FetchType.EAGER, optional = true)//用于声明属性的存取策略

     * @Basic 基本属性类型映射,注解于非Static 非transient的属性,
     * 这时候我们可以为其声明抓取策略等属性
     * fetch: 获取策略,当以session.get()方法获取数据库对象时:
     * FetchType.LAZY为懒加载,会在第一次使用该属性(如调用getAge()方法)时才获取。
     * FetchType.EAGER为即时加载。
     * optional:表示当前属性是否可选,默认为true;如果为false,则在持久化到数据库时,如果此属性为null,则会失败
    

    @Transient - 可选,自定义字段,表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性,如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则ORM 框架默认其注解为 @Basic

    示例 : 
    // 根据 birth 计算出 age 属性     
    @Transient 
    public int getAge() { 
       return getYear(new Date()) - getYear(birth);    
    }
    

    @Lob

     * @Lob 注解表示属性将被持久化为Blob或者Clob类型,
     * 具体取决于属性的类型, java.sql.Clob, Character[], char[] 和 java.lang.String这些类型的属性都被持久化为Clob类型,
     * 而java.sql.Blob, Byte[], byte[] 和 serializable类型则被持久化为Blob类型.
    

    @Temporal(TemporalType.TIME)//用于定义映射到数据库的时间精度

    * @Temporal(TemporalType=DATE)       日期 
    * @Temporal(TemporalType=TIME)       时间 
    * @Temporal(TemporalType=TIMESTAMP) 两者兼具
    
    表关系模式:

    ?对于多对一(不管是单向关联还是双向关联),都只需要在多的一端使用@ManyToONe修饰关联实体的属性

    单向: 关系写哪边,就由谁管理。

    一对多,多对一

       1. 多对一:关系映射再多端维护。如:论文类别--->类别
       2. 一对多: 关系映射再一端维护。如:用户--->电子邮件
       3. 数据库表设计:都是再多的一端建立外键
    

    对于单向的1—N关联关系。只需要在1的一端加入set类型的成员变量,记录所有的关联的实体。
    N的端不维护关系,没得变化~

        //多对一:多个人共住一个地址。地址实体不维护关系
        // 定义该Person实体关联的Address实体
        @ManyToOne(targetEntity=Address.class)
        // 映射外键列,指定外键列的列名为address_id、不允许为空
        @JoinColumn(name="address_id" , nullable=false)
        @Cascade(CascadeType.ALL)
        private Address address;
    
        //一对多:一个人有多个房子。
        // 定义该Person实体所有关联的Address实体,没有指定cascade属性
        @OneToMany(targetEntity=Address.class)
        // 映射外键列,此处映射的外键列将会添加到关联实体对应的数据表中,为啥呢?
        @JoinColumn(name="person_id" , referencedColumnName="person_id")
        private Set<Address> addresses = new HashSet<>();
    

    一对一

        //一对一:一个人住一个地址。
        // 定义该Person实体关联的Address实体
        @OneToOne(targetEntity=Address.class)
        // 映射名为address_id的外键列,参照关联实体对应表的addres_id主键列
        @JoinColumn(name="address_id", referencedColumnName="address_id" , unique=true)
        private Address address;
        地址肯定是独一无二的嘛,对不对!增加unique约束~
        
    

    多对多

    
    
    双向:

    一对多,多对一

        1. 不要让1的端(也就是有Set这个集合的那一个)控制关联的关系。而是使用N的一端控制关联关系,一般由多方管理。
        2. 两端都需要增加对关联属性的访问,N的一端增加引用关联实体的属性,1的一端增加集合属性,集合元素为关联的实体.
        3. 只需要在N的一端增加一个外键列就好啦。因此在多的一端,使用@JoinColumn来映射外键列。
        4. 我们使用@OneToMany的时候中应该指明mappedBy (映射由谁控制) 属性,表明当前的实体不能控制关联的关系
        5. 非关系控制实体类,不能指明@JoinColumn 或@JointTable 来修饰关联实体的属性
    

    一对多,多对一双向实例:

        //一端,非控制端
        // 定义该Person实体所有关联的Address实体
        // 指定mappedBy属性表明该Person实体不控制关联关系
        //这里的person是address中的person
        @OneToMany(targetEntity=Address.class, mappedBy="person")
        private Set<Address> addresses = new HashSet<>();
        
         //多端,关系控制端
        // 定义该Address实体关联的Person实体
        @ManyToOne(targetEntity=Person.class)
        // 定义名为person_id外键列,该外键列引用person_inf表的person_id列。
        @JoinColumn(name="person_id" , referencedColumnName="person_id", nullable=false)
        private Person person;
    

    多对多

      1. 双向的N-N关联,两端都要set分别使用@ManyToMany 都要使用@JoinTable 显示的连接在一起
      2. 哪端想放弃控制关联关系,可以使用mappBy。也就不能使用@joinTable啦
    
        //person实体类
        @ManyToMany(targetEntity=Address.class)
        // 映射连接表,指定连接表的表名为person_address
        @JoinTable(name="person_address",
            // 映射连接表中名为person_id的外键列,
            // 该列参照当前实体对应表的person_id主键列
            joinColumns=@JoinColumn(name="person_id", referencedColumnName="person_id"),
            // 映射连接表中名为address_id的外键列,
            // 该列参数当前实体的关联实体对应表的address_id主键列
            inverseJoinColumns=@JoinColumn(name="address_id", referencedColumnName="address_id")
        )
        private Set<Address> addresses = new HashSet<>();
        
        //address实体类
        // 定义该Address实体所有关联的Person实体
        @ManyToMany(targetEntity=Person.class)
        // 映射连接表,指定连接表的表名为person_address
        @JoinTable(name="person_address",
            // 映射连接表中名为address_id的外键列,
            // 该列参照当前实体对应表的address_id主键列
            joinColumns=@JoinColumn(name="address_id", referencedColumnName="address_id"),
            // 映射连接表中名为person_id的外键列,
            // 该列参照当前实体对应表的person_id主键列
            inverseJoinColumns=@JoinColumn(name="person_id", referencedColumnName="person_id")
        )
        private Set<Person> persons = new HashSet<>();
    

    上边的代码例子是谁都没有放弃控制权;没毛病,这样是可以的,下边看一下具体使用:

      我先持久化了person,创建关联关系。最后持久化地址。  
      // 创建一个Person对象
      Person p = new Person();
      // 持久化Person对象(对应于插入主表记录)
      session.save(p);
      // 创建一个瞬态的Address对象
      Address a = new Address("遵义市海龙坝");
      // 先设置Person和Address之间的关联关系
      a.getPersons().add(p);
      // 再持久化Address对象
      session.persist(a);
      
      上边的例子也可以这样:先创建插入地址,再插入到关系表中。
      session.save(a); 
      a.getPersons().add(p);  
    

    一对一:
    基本上一对一都是基于外键使用,很少有共享主键的使用场景,所以暂不讨论。
    总有一方要先把实体存到数据库中,然后另一方才有外键存在呢。

    放弃控制权的一方,可以不考虑address属性,增加新的记录

        // 定义该Person实体关联的Address实体
        @OneToOne(targetEntity=Address.class , mappedBy="person")
        private Address address;
    
        //实际使用
        Person p=new Person(); 
        session.save(p)没有压力 
    

    控制关联关系一方, 增加记录有先后顺序的。必须先有person再有address

        // 定义该Address实体关联的Person实体
        @OneToOne(targetEntity=Person.class)
        // 用于映射person_id外键列,参照person_inf表的person_id列
        // 指定了unique=true表明是1-1关联
        @JoinColumn(name="person_id" , referencedColumnName="person_id"
            , unique=true)
        private Person person;
        
        //现实中新增address的例子
        Address address=new Address(); 
        address.addPerson(持久化的Person); 
        session.save(address) 
    
    关联映射常用注解

    @OneToOne、@OneToMany、@ManyToOne、ManyToMany的共有属性:

    1. fetch - 配置加载方式。取值有:
      Fetch.EAGER - 及时加载,多对一默认是Fetch.EAGER
      Fetch.LAZY - 延迟加载,一对多默认是Fetch.LAZY

    2. cascade - 设置级联方式,取值有:
      CascadeType.PERSIST - 保存
      CascadeType.REMOVE - 删除
      CascadeType.MERGE - 修改
      CascadeType.REFRESH - 刷新
      CascadeType.ALL - 全部
      举个例子:Order 和OrderItem有级联关系,那么删除Order时将同时删除它所对应的OrderItem对象。而如果OrderItem还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。

    3. targetEntity - 配置集合属性类型,如:@OneToMany(targetEntity=Book.class)

    4. mappedBy :@OneToMany(mappedBy="对方") //反向配置,对方管理
      定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题。该属性的值是“多”方class里的“一”方的变量名,也就是多方表中的外键名称。
      一旦被注解@mapperBy,即放弃了维护关联关系,而@JoinColumn注解的都是在“主控方”,因而我们需要注解在Article类中.
      对于不需要维护这种关系的从表则通过mappedBy属性进行声明,mappedBy的值指向主体(owner)端的对象。如:mappedby="role"

    5. optional属性
      是定义该关联类是否必须存在,值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为null。optional属性的默认值是true。
      optional 属性实际上指定关联类与被关联类的join 查询关系,如optional=false 时join 查询关系为inner join,optional=true 时join 查询关系为leftjoin。

    @JoinColumn - 可选,用于描述一个关联的字段。Hibernate使用@JoinColumn来修饰代表关联实体的属性,用于映射底层的外键列。

      @JoinColumn和@Column类似,这里描述的不是一个简单字段,而是一个关联字段,
      例如描述一个@ManyToOne 的字段。
      属性: 
      name - 该字段的名称,由于@JoinColumn描述的是一个关联字段,如ManyToOne, 则默认的名称由其关联的实体决定。 
      例如,实体 Order 有一个user 属性来关联实体 User, 则 Order 的 user 属性为一个外键,  其默认的名称为实体
                User的名称 + 下划线 + 实体User的主键名称
      unique=true - 外键的值是唯一的(unique),不可重复,与另一类的主键一致。
    

    相关文章

      网友评论

          本文标题:java web实战学习第一天

          本文链接:https://www.haomeiwen.com/subject/oyykzxtx.html