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 > @ElementCollection Mapped 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值,即使可能也是不推荐的.
posted @ 2016-07-28 15:25  wang_jingj  阅读(1282)  评论(0编辑  收藏  举报