Pro JPA2读书笔记系列(五)-第五章(集合映射)

Pro JPA2 第五章(集合映射)

  • 5.1关系和元素集合
    映射集合实际上存在三种可以存储的对象:映射实体的,可嵌入的和基本类型的集合.
    当源实体用友一个包含目标实体类型的实例集合时,称之为一个多值关系.然儿,可嵌入的集合和基本类型的集合不是关系,它们只是元素的集合,因而称之为元素集合(element collection).关系定义了独立实体之间的关联,而元素集合包含了依赖于引用实体的对象,并且只能通过包含它们的实体进行检索.
    关系和元素集合之间的一个世纪区别在于用来表示它们的注解.关系至少要求采用关系注解,@OneToMany或者@ManyToMany,而一个元素集合是由@ElementCollection注解来标识.

    @Embeddable
    public class VacationEntry {
        @Temporal(TemporalType.DATE)
        private Calendar startDate;
    
        @Column(name="DAYS")
        private int daysTaken;
    }
    
    @Entity
    public class Employee {
        @Id
        private int id;
        private long salary;
    
        @ElementCollection(targetClass=VacationEntry.class)
        private Collection vacationBookings;
    
        @ElementCollection
        private Set<String> nickNames;
    }

    在Employee中没有映射任何额外的元数据,回顾一下,存储在集合中的元素不是实体,所以它们不具有任何映射表.嵌入对象应该与引用它们的实体存储在同一个表中,但是如果有一个嵌入对象的集合,那么如何在单行中存储多个可能映射的对象?类似地,对于基本类型不能把每个呢成String都映射到Employee表中第一列,并期望在单个行中存储多个字符串.为此,元素集合需要一个单独的表,称之为集合表(Collection table). 没个集合表必须有一个引用包含它的实体表的联结列.结合表中的其他列用于映射可嵌入元素的特性,或者如果该元素是一个基本类型,那么映射基本元素的状态.
    我们使用@CollectionTable注解来指定集合表,它允许我们指定表的名称和联结列.
    使用@AttributeOverride注解来重写列名.

    @Entity
    public class Employee {
        @Id
        private int id;
        private long salary;
    
        @ElementCollection(targetClass=VacationEntry.class)
        @CollectionTable(name="VACATION",joinColumns=@JoinColumn(name="EMP_ID"))
        @AttributeOverride(name="daysTaken",column=@Column(name="DAYS_ABS"))
        private Collection vacationBookings;
    
        @ElementCollection
        @Column(name="NICKNAME")
        private Set<String> nickNames;
    }

    EMPLOYEE实体表和包含重写的映射集合表enter image description here

  • 5.2 使用不同的集合类型
    可以使用的集合类型由:Collection,Set,List和Map.下面将一个一个的介绍.

    • 5.2.1 Set或者Collection
      当不关注底层的实现且普通的Collection方法足够用于访问它所存储的实体时,可以使用Collection.
      Set可以防止插入重复的元素.
    • 5.2.2 List

      • 通过实体或元素的特性排序
        对List中实体或元素排序的最常见的方法是:根据特定的实体或元素特性的比较,制定一条排序规则,如果List是一个关系,那么特性通常是目标实体的主键.
        在@OrderBy注解中指示用于排序的特性.如果List是一个关系并且引用实体,其中实体由不带字段或者属性的@OrderBy指定或者根本不指定,那么List将根据List中实体的主键排序.
        @Entity
        public class Department {
        
            @OneToMany(mappedBy="department")
            @OrderBy("name ASC,id DESC")
            private List<Employee> employees;
        }

      其中,@OrderBy注解中包含ASC不是必需的,默认情况下就是升序.

    • 5.2.3 Map

      • 键和值
        虽然基本类型,可嵌入类型或实体类型都可以作为Map键,但是,他们必需遵循键的基本规则,他们必须是可比的,并且可适当地相应hashCode()方法,以及在必要时相应equals()方法.他们也应该是唯一的.
        当键是基本的或可嵌入的类型时,它们直接存储在所引用的表中.根据映射的类型,它可以是目标实体表,联接表或者集合表.
      • 以基本类型为键

        @Entity
        public class Employee {
            @Id
            private int id;
            private long salary;
        
            @ElementCollection
            @CollectionTable(name="EMP_PHONE")
            @MapKeyColumn(name="PHONE_TYPE")
            @Column(name="PHONE_NUM")
            private Map<String,String> phoneNumbers;
        }

        @MapKeyColumn用来指示在集合表中存储基本键的列.当未指定直接时,存储键的列名为所映射的集合特性,再附件”_KEY”后缀.
        EMPLOYEE_ID和PHONE_TYPE列的主键限制.enter image description here
        使用枚举类型来代替String类型.

        public enmu PhoneType {
            Home,Mobile,Work
        }
        
        @Entity
        public class Employee {
            @Id
            private int id;
            private long salary;
        
            @ElementCollection
            @CollectionTable(name="EMP_PHONE")
            @MapKeyEnumerated(EnumType.STRING)
            @MapKeyColumn(name="PHONE_TYPE")
            @Column(name="PHONE_NUM")
            prvate Map<PhoneType,String> phoneNumbers;
        }

        以String为键的Map的一对多关系

        @Entity
        public class Department{
            @Id
            private int id;
        
            @OneToMany(mappedBy="department")
            @MapKeyColumn(name="CUB_ID")
            private Map<String,Employee> employeesByCubicle;
        }

        以String为键的Map的多对多关系

        @Entity
        public class Department {
            @Id
            private int id;
            private String name;
        
            @ManyToMany
            @JoinTable(name="DEPT_EMP",
                        joinColumns=@JoinColumn(name="DEPT_ID"),
                        inverseJoinColumns=@JoinColumn(name="EMP_ID"))
            @MapKeyColumn(name="CUB_ID")
            private Map<String,Employee> employeesByCubicle;
        }

        EMPLOYEE和DEPARTMENT实体表以及DEPT_EMP联接表.enter image description here

      • 以实体特性为键
        当实体的一对多或多对多关系集合表示为一个Map时,它通常以目标实体类型的某些特性为键,以实体特性为键实际上是一个以基本类型为键的特例,其中映射是一个关系,而且键的基本类型是目标实体上特性的类型.我们可以使用@MapKey注解来指定目标实体上作为键的特性.

        @Entity
        public class Department {
        
            @OneToMany(mappedBy="department")
            @MapKey(name="id")
            private Map<Integer,Employee> employees;
        }
      • 以可嵌入的类型为键
        这个不推荐使用.
      • 以实体为键
        这个不推荐使用.
      • 非型化Map
        如果不想使用Map< KeyType,ValueType >的类型化参数版本,那么将使用无参样式的Map来定义它.

        @Entity
        public class Department {
            @OneToMany(targetEntity=Employee.class,mappedBy="department")
            @MapKey
            private Map employees;
        }

        以String为键的非类型化String元素集合.

        @Entity
        public class Employee {
            @Id
            private int id;
            private long salary;
        
            @ElementCollection(targetClass=String.class)
            @ColectionTable(name="EMP_PHONE")
            @MapKeyColumn(name="PHONE_TYPE")
            @MapKeyClass(String.class)
            @Column(name="PHONE_NUM")
            private Map phoneNumbers;
            }
      • 映射规则
        使用Map的一些基本规则:

        • 当使用非类型化Map时,利用关系的@MapKeyClass和targetEntity/targetClass元素,以及元素集合映射来指定类.
        • 当Map以目标实体的特性为键时,将@MapKey用于一对多或多对多的关系Map.
        • 使用@MapKeyJoinColumn来重写实体键的联结列.
        • 当值为基本类型的元素集合时,将@Column重写用于存储值的列.
        • 当键为基本类型时,使用@MapKeyColumn来重写存储键的值.
        • 如果需要进一步地限定基本的时间或枚举类型的键,那么使用@MapKeyTemporal和@MapKeyEnumerated.
        • 使用带”key.”或”value.”前缀的@AttributeOverride,以分别为Map键或值重写其可嵌入特性类型的列.

          Map映射键注解值注解
          Map< Basic,Basic >@ElementCollection@MapKeyColumn
          @MapKeyEnumerated
          @MapKeyTemporal
          @Column
          Map< Basic,Embeddable >@ElementCollection@MapKeyColumn
          @MapKeyEnumerated
          @MapKeyTemporal
          由可嵌入对象映射,@AttributeOverride
          @AssociationOverride
          Map< Basic,Entity >@OneToMany
          @ManyToMany
          @MapKey
          @MapKeyColumn
          @MapKeyEnumerated
          @MapKeyTemporal
          由实体映射
          Map< Embeddable,Basic >@ElementCollectionMapped by embeddable,@AttributeOverride@Column
          Map< Embeddable,Embeddable >@ElementCollection由可嵌入对象映射,@AttributeOverride由可嵌入对象映射,@AttributeOverride
          @AssociationOverride
          Map< Embeddable,Entity >@OneToMany
          @ManyToMany
          由可嵌入对象映射由实体映射
          Map< Entity,Basic >@ElementCollection@MapKeyJoinColumn@Column
          Map< Entity,Entity >@OneToMany
          @ManyToMany
          @MapKeyJoinColumn由实体映射
  • 最佳实践

    • 当使用一个List时,如果没有明确指定任何顺序,不要假设它会自动排序,数据库结果会影响List顺序.
    • 一般可以通过它们自身的特性之一给对象排序.使用@OrderBy注解总是最佳方法.
    • Map类型很有帮助,但是它们很复杂.如果达到了一定的层次,使用Map将会非常好.
    • 如果List一样,Map的首选和最有效的使用方法是以目标对象的特性为键,因此以基本的特性类型为键的实体Map是最为常见的 和最有用的.
    • 避免在Map中使用嵌入对象,尤其是作为键,因为它们的标识通常是没有定义的
    • 不能保证在集合中会支持重复或null值,即使可能也是不推荐的.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值