类图和ORM关系(三)

    技术2022-05-11  77

    3         关联映射 3.1    单向ManyToOne关联 这是实体间最基本、最重要的关联,在数据库表间建立外键关系。理论上可以只使用ManyToOne关联而不用其他关联来实现整个应用;另一方向的关联可以通过查询(query)实现。 Employee类和Company类是ManyToOne的关系,例子如下: @Entity public   class  Company1  {    @Id private int id;    private String companyName;} @Entity public   class  Employee1  {    @Id private int id;    private String name;    @ManyToOne(optional = false)     private Company1 company;} 数据库表如下:create table  " COMPANY1 " (     " ID "  INTEGER not  null ,    " COMPANYNAME "  VARCHAR( 255 ),    constraint  " SYS_PK_1992 "  primary key ( " ID " ));create unique index  " SYS_PK_1992 "  on  " COMPANY1 " ( " ID " );create table  " EMPLOYEE1 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    " COMPANY_ID "  INTEGER not  null ,    constraint  " SYS_PK_2006 "  primary key ( " ID " ));alter table  " EMPLOYEE1 "       add constraint  " FK14AC0F233D3817C "      foreign key ( " COMPANY_ID " )     references  " COMPANY1 " ( " ID " );create unique index  " SYS_PK_2006 "  on  " EMPLOYEE1 " ( " ID " );create index  " SYS_IDX_2075 "  on  " EMPLOYEE1 " ( " COMPANY_ID " );   3.2    单向OneToMany关联 OneToMany是涉及到集合的最重要的关联,可以采用Set、Collection和List来表示集合。 Set实现集合的例子如下: @Entity public   class  Employee2  {    @Id private int id;    private String name;} @Entity public   class  Company2  {    @Id private int id;    private String companyName;    @OneToMany private Set<Employee2> employees = new HashSet();    } 数据库表如下:create table  " EMPLOYEE2 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2007 "  primary key ( " ID " ));create unique index  " SYS_PK_2007 "  on  " EMPLOYEE2 " ( " ID " );create table  " COMPANY2 " (     " ID "  INTEGER not  null ,    " COMPANYNAME "  VARCHAR( 255 ),    constraint  " SYS_PK_1993 "  primary key ( " ID " ));create unique index  " SYS_PK_1993 "  on  " COMPANY2 " ( " ID " );create table  " COMPANY2_EMPLOYEE2 " (     " COMPANY2_ID "  INTEGER not  null ,    " EMPLOYEES_ID "  INTEGER not  null ,    constraint  " SYS_PK_1995 "  primary key ( " COMPANY2_ID " , " EMPLOYEES_ID " ));alter table  " COMPANY2_EMPLOYEE2 "       add constraint  " FK4BD4B2DA17FCDBA5 "      foreign key ( " COMPANY2_ID " )     references  " COMPANY2 " ( " ID " );alter table  " COMPANY2_EMPLOYEE2 "       add constraint  " FK4BD4B2DA1F5EA2AE "      foreign key ( " EMPLOYEES_ID " )     references  " EMPLOYEE2 " ( " ID " );create unique index  " SYS_IDX_SYS_CT_1994_1996 "  on  " COMPANY2_EMPLOYEE2 " ( " EMPLOYEES_ID " );create unique index  " SYS_PK_1995 "  on  " COMPANY2_EMPLOYEE2 " ( " COMPANY2_ID " , " EMPLOYEES_ID " );create index  " SYS_IDX_2065 "  on  " COMPANY2_EMPLOYEE2 " ( " COMPANY2_ID " );create index  " SYS_IDX_2067 "  on  " COMPANY2_EMPLOYEE2 " ( " EMPLOYEES_ID " );   采用Collection实现集合只是用Collection代替Set。如果采用List实现集合,集合中的元素包括位置信息,但是需要Hibernate特有的Annotation支持: @Entity public   class  Employee3  {    @Id private int id;    private String name;} @Entity public   class  Company3  {    @Id private int id;    private String companyName;    @OneToMany    @org.hibernate.annotations.IndexColumn(name = "EMPLOYEE_POSITION")    private List<Employee3> employees = new ArrayList<Employee3>();    } 数据库表与上面的例子比,COMPANY3_EMPLOYEE3表中多了EMPLOYEE_POSITION字段。   3.3    双向ManyToOne关联 如果需要实现双向关联,可以增加另一个方向的关联。 @Entity public   class  Employee4  {    @Id private int id;    private String name;    @ManyToOne Company4 company;} @Entity public   class  Company4  {   @Id private int id;   private String name;   @OneToMany(mappedBy="company")    private Set<Employee4> employees = new HashSet();   } 数据库表如下:create table  " COMPANY4 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2001 "  primary key ( " ID " ));create unique index  " SYS_PK_2001 "  on  " COMPANY4 " ( " ID " );create table  " EMPLOYEE4 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    " COMPANY_ID "  INTEGER,    constraint  " SYS_PK_2009 "  primary key ( " ID " ));alter table  " EMPLOYEE4 "       add constraint  " FK14AC0F263D3817F "      foreign key ( " COMPANY_ID " )     references  " COMPANY4 " ( " ID " );create unique index  " SYS_PK_2009 "  on  " EMPLOYEE4 " ( " ID " );create index  " SYS_IDX_2077 "  on  " EMPLOYEE4 " ( " COMPANY_ID " ); 需要注意的是,为了正确持久化,双方对象需要相互引用;mappedBy属性说明ManyToOne和OneToMany是镜像关系,如果没有该属性Hibernate可能会有不必要的更新。还有正确cascade属性,保证关联对象自动更新。 对于可选的Many关系(Zero或Many关系),如果不希望Employee类中的company字段可以是null,则可以通过@JoinTable采用中间表实现: @Entity public   class  Company5  {    @Id private int id;    private String companyName;    @OneToMany(mappedBy = "company")    private Set<Employee5> employees = new HashSet<Employee5>();    } @Entity public   class  Employee5  {    @Id private int id;    private String name;    @ManyToOne    @JoinTable(name = "COMPANY5_EMPLOYEE5",        joinColumns = {@JoinColumn(name = "EMPLOYEE_ID")},        inverseJoinColumns = {@JoinColumn(name = "COMPANY_ID")})    private Company5 company;    } 数据库表如下:create table  " EMPLOYEE5 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2010 "  primary key ( " ID " ));create unique index  " SYS_PK_2010 "  on  " EMPLOYEE5 " ( " ID " );create table  " COMPANY5 " (     " ID "  INTEGER not  null ,    " COMPANYNAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2002 "  primary key ( " ID " ));create unique index  " SYS_PK_2002 "  on  " COMPANY5 " ( " ID " );create table  " COMPANY5_EMPLOYEE5 " (     " EMPLOYEE_ID "  INTEGER not  null ,    " COMPANY_ID "  INTEGER,    constraint  " SYS_PK_1982 "  primary key ( " EMPLOYEE_ID " ));alter table  " COMPANY5_EMPLOYEE5 "       add constraint  " FKB3F547403D38180 "      foreign key ( " COMPANY_ID " )     references  " COMPANY5 " ( " ID " );alter table  " COMPANY5_EMPLOYEE5 "       add constraint  " FKB3F54740BCD0EEE8 "      foreign key ( " EMPLOYEE_ID " )     references  " EMPLOYEE5 " ( " ID " );create unique index  " SYS_PK_1982 "  on  " COMPANY5_EMPLOYEE5 " ( " EMPLOYEE_ID " );create index  " SYS_IDX_2057 "  on  " COMPANY5_EMPLOYEE5 " ( " EMPLOYEE_ID " );create index  " SYS_IDX_2059 "  on  " COMPANY5_EMPLOYEE5 " ( " COMPANY_ID " );   3.4    单向OneToOne关联 OneToOne关联不是常用的关联。一般地如User类和Address类采用值类型映射;如果Address类还被其他类关联,则Address类需要实现为实体。这时候User类和Address类就是OneToOne关联。 @Entity public   class  Address6  {    @Id private int id;    private String street;} @Entity public   class  User6  {    @Id private int id;    private String name;    @OneToOne private Address6 homeAddress;    } 数据库表如下:create table  " ADDRESS6 " (     " ID "  INTEGER not  null ,    " STREET "  VARCHAR( 255 ),    constraint  " SYS_PK_1967 "  primary key ( " ID " ));create unique index  " SYS_PK_1967 "  on  " ADDRESS6 " ( " ID " );create table  " USER6 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    " HOMEADDRESS_ID "  INTEGER,    constraint  " SYS_PK_2026 "  primary key ( " ID " ));alter table  " USER6 "       add constraint  " FK4E39DAB932C1432 "      foreign key ( " HOMEADDRESS_ID " )     references  " ADDRESS6 " ( " ID " );create unique index  " SYS_PK_2026 "  on  " USER6 " ( " ID " );create index  " SYS_IDX_2085 "  on  " USER6 " ( " HOMEADDRESS_ID " );   OneToOne的一方如果是可选的,也就是说One to Zero或One。这种关联可以采用中间表方式实现,又可以分为JointTable和SecondaryTable方式。JoinTable方式例子如下: @Entity public   class  Address7  {    @Id private int id;    private String street;} @Entity public   class  User7  {    @Id private int id;    private String name;        @OneToOne    @JoinTable(        name="User7_Address7",        joinColumns = @JoinColumn(name = "User_ID"),        inverseJoinColumns = @JoinColumn(name = "Address_ID")     )    private Address7 homeAddress;} 数据库表如下:create table  " ADDRESS7 " (     " ID "  INTEGER not  null ,    " STREET "  VARCHAR( 255 ),    constraint  " SYS_PK_1968 "  primary key ( " ID " ));create unique index  " SYS_PK_1968 "  on  " ADDRESS7 " ( " ID " );create table  " USER7 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2027 "  primary key ( " ID " ));create unique index  " SYS_PK_2027 "  on  " USER7 " ( " ID " );create table  " USER7_ADDRESS7 " (     " USER_ID "  INTEGER not  null ,    " ADDRESS_ID "  INTEGER,    constraint  " SYS_PK_2029 "  primary key ( " USER_ID " ));alter table  " USER7_ADDRESS7 "       add constraint  " FKB6D07C1661646A14 "      foreign key ( " ADDRESS_ID " )     references  " ADDRESS7 " ( " ID " );alter table  " USER7_ADDRESS7 "       add constraint  " FKB6D07C16BC1189B0 "      foreign key ( " USER_ID " )     references  " USER7 " ( " ID " );create unique index  " SYS_PK_2029 "  on  " USER7_ADDRESS7 " ( " USER_ID " );create index  " SYS_IDX_2087 "  on  " USER7_ADDRESS7 " ( " USER_ID " );create index  " SYS_IDX_2089 "  on  " USER7_ADDRESS7 " ( " ADDRESS_ID " ); SecondaryTable方式的例子如下: @Entity public   class  Address8  {    @Id private int id;    private String street;} @Entity@SecondaryTable(name  =   " User8_Address8 " ) public   class  User8  {    @Id private int id;    private String name;        @OneToOne    @JoinColumn(table = "User8_Address8", name = "Address_ID")    private Address8 homeAddress;    } 数据库表结构和上面的例子类似,不再重复。   3.5    双向OneToOne关联 双向OneToOne关联采用外键关联,例子如下: public   class  Address9  {    @Id private int id;    private String street;    @OneToOne(mappedBy="homeAddress")    private User9 user;} @Entity public   class  User9  {    @Id private int id;    private String name;    @OneToOne private Address9 homeAddress;    } 数据库表如下:create table  " ADDRESS9 " (     " ID "  INTEGER not  null ,    " STREET "  VARCHAR( 255 ),    constraint  " SYS_PK_1970 "  primary key ( " ID " ));create unique index  " SYS_PK_1970 "  on  " ADDRESS9 " ( " ID " );create table  " USER9 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    " HOMEADDRESS_ID "  INTEGER,    constraint  " SYS_PK_2032 "  primary key ( " ID " ));alter table  " USER9 "       add constraint  " FK4E39DAE932C1435 "      foreign key ( " HOMEADDRESS_ID " )     references  " ADDRESS9 " ( " ID " );create unique index  " SYS_PK_2032 "  on  " USER9 " ( " ID " );create index  " SYS_IDX_2095 "  on  " USER9 " ( " HOMEADDRESS_ID " );   3.6    单向ManyToMany关联 单向ManyToMany采用中间表实现,包括Set、Collection和List三种方式,其中Set中没有重复元素,Collection可以包括重复元素,List可以记录元素的位置。Collection和List方式都要用到Hibernate特有的Annotation。Set的例子如下: @Entity public   class  Item1  {    @Id private int id;    private String description;} @Entity public   class  Category1  {    @Id private int id;    private String name;    @ManyToMany    @JoinTable(        name = "CATEGORY1_ITEM1",        joinColumns = {@JoinColumn(name = "CATEGORY_ID")},        inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")}    )    private Set<Item1> items = new HashSet<Item1>();    } 数据库表如下:create table  " ITEM1 " (     " ID "  INTEGER not  null ,    " DESCRIPTION "  VARCHAR( 255 ),    constraint  " SYS_PK_2011 "  primary key ( " ID " ));create unique index  " SYS_PK_2011 "  on  " ITEM1 " ( " ID " );create table  " CATEGORY1 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_1985 "  primary key ( " ID " ));create unique index  " SYS_PK_1985 "  on  " CATEGORY1 " ( " ID " );create table  " CATEGORY1_ITEM1 " (     " CATEGORY_ID "  INTEGER not  null ,    " ITEM_ID "  INTEGER not  null ,    constraint  " SYS_PK_1977 "  primary key ( " CATEGORY_ID " , " ITEM_ID " ));alter table  " CATEGORY1_ITEM1 "       add constraint  " FK43420332A218AA44 "      foreign key ( " CATEGORY_ID " )     references  " CATEGORY1 " ( " ID " );alter table  " CATEGORY1_ITEM1 "       add constraint  " FK434203324250AF1A "      foreign key ( " ITEM_ID " )     references  " ITEM1 " ( " ID " );create unique index  " SYS_PK_1977 "  on  " CATEGORY1_ITEM1 " ( " CATEGORY_ID " , " ITEM_ID " );create index  " SYS_IDX_2035 "  on  " CATEGORY1_ITEM1 " ( " ITEM_ID " );create index  " SYS_IDX_2037 "  on  " CATEGORY1_ITEM1 " ( " CATEGORY_ID " ); Collection的例子如下: @Entity public   class  Item2  {    @Id private int id;    private String description;} @Entity public   class  Category2  {    @Id private int id;    private String name;        @ManyToMany    @org.hibernate.annotations.CollectionId(        columns = @Column(name = "CATEGORY_ITEM_ID"),        type = @org.hibernate.annotations.Type(type = "long"),        generator = "native")    @JoinTable(        name = "CATEGORY2_ITEM2",        joinColumns = {@JoinColumn(name = "CATEGORY_ID")},        inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")})    private Collection<Item2> items = new ArrayList<Item2>();    } 数据库与前面的例子类似,不再重复。 List的例子如下: @Entity public   class  Item3  {    @Id private int id;    private String description;} @Entity public   class  Category3  {    @Id private int id;    private String name;        @ManyToMany    @JoinTable(        name = "CATEGORY3_ITEM3",        joinColumns = {@JoinColumn(name = "CATEGORY_ID")},        inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")})    @org.hibernate.annotations.IndexColumn(name = "ITEM_POSITION")    private List<Item3> items = new ArrayList<Item3>();    } 数据库和上面的例子类似,不再给出。   3.7    双向ManyToMany关联 双向ManyToMany关联 最好采用两个OneToMany来实现。当然采用ManyToMany关联也是可以的。例子如下: @Entity public   class  Item4  {    @Id private int id;    private String description;    @ManyToMany(mappedBy = "items")    private Set<Category4> categories = new HashSet<Category4>();    } @Entity public   class  Category4  {    @Id private int id;    private String name;        @ManyToMany    @JoinTable(        name = "CATEGORY4_ITEM4",        joinColumns = {@JoinColumn(name = "CATEGORY_ID")},        inverseJoinColumns = {@JoinColumn(name = "ITEM_ID")}    )    private Set<Item4> items = new HashSet<Item4>();    } 数据库表和前面的例子类似,不再给出。   如果在双向关联中,还有其他字段信息,则可以采用值对象JoinTable或中间实体JoinTable实现。下面的例子假定在Category类和Item类关联时还需要增加日期信息。值对象JoinTable方式相对简单,但是需要用到Hibernate特有的Annotation,例子如下: @Entity public   class  Item5  {    @Id private int id;    private String description;} @Embeddable public   class  CategorizedItem5  {    @org.hibernate.annotations.Parent // Optional back-pointer    private Category5 category;    @ManyToOne    @JoinColumn(name = "ITEM_ID", nullable = false, updatable = false)    private Item5 item;    private Date dateAdded;    public Category5 getCategory() return category;}    public void setCategory(Category5 category) this.category = category; }} @Entity public   class  Category5  {    @Id private int id;    private String name;        @org.hibernate.annotations.CollectionOfElements    @JoinTable(        name = "CategorizedItem5",        joinColumns = @JoinColumn(name = "CATEGORY_ID"))    private Set<CategorizedItem5> categorizedItems = new HashSet<CategorizedItem5>();    } 数据库表如下:create table  " ITEM5 " (     " ID "  INTEGER not  null ,    " DESCRIPTION "  VARCHAR( 255 ),    constraint  " SYS_PK_2015 "  primary key ( " ID " ));create unique index  " SYS_PK_2015 "  on  " ITEM5 " ( " ID " );    create table  " CATEGORY5 " (         " ID "  INTEGER not  null ,        " NAME "  VARCHAR( 255 ),        constraint  " SYS_PK_1989 "  primary key ( " ID " )    );create unique index  " SYS_PK_1989 "  on  " CATEGORY5 " ( " ID " );create table  " CATEGORIZEDITEM5 " (     " CATEGORY_ID "  INTEGER not  null ,    " ITEM_ID "  INTEGER not  null ,    " DATEADDED "  TIMESTAMP,    constraint  " SYS_PK_1983 "  primary key ( " CATEGORY_ID " , " ITEM_ID " ));alter table  " CATEGORIZEDITEM5 "       add constraint  " FKD6EE8B77A218AA48 "      foreign key ( " CATEGORY_ID " )     references  " CATEGORY5 " ( " ID " );alter table  " CATEGORIZEDITEM5 "       add constraint  " FKD6EE8B774250AF1E "      foreign key ( " ITEM_ID " )     references  " ITEM5 " ( " ID " );create unique index  " SYS_PK_1983 "  on  " CATEGORIZEDITEM5 " ( " CATEGORY_ID " , " ITEM_ID " );create index  " SYS_IDX_2061 "  on  " CATEGORIZEDITEM5 " ( " ITEM_ID " );create index  " SYS_IDX_2063 "  on  " CATEGORIZEDITEM5 " ( " CATEGORY_ID " ); 中间实体JoinTable方式的例子如下: @Entity public   class  Item6  {    @Id private int id;    private String description;    @OneToMany(mappedBy = "item")    private Set<CategorizedItem6> categorizedItems = new HashSet<CategorizedItem6>();      } @Entity public   class  Category6  {    @Id private int id;    private String name;        @OneToMany(mappedBy = "category")    private Set<CategorizedItem6> categorizedItems = new HashSet<CategorizedItem6>();    } @Entity public   class  CategorizedItem6  {    @Embeddable    public static class Id implements Serializable {        @Column(name = "CATEGORY_ID")        private Long categoryId;        @Column(name = "ITEM_ID")        private Long itemId;        public Id() {}        public Id(Long categoryId, Long itemId) {            this.categoryId = categoryId;            this.itemId = itemId;        }        public boolean equals(Object o) {            if (o != null && o instanceof Id) {                Id that = (Id)o;                return this.categoryId.equals(that.categoryId) &&                       this.itemId.equals(that.itemId);            } else {                return false;            }        }        public int hashCode() {            return categoryId.hashCode() + itemId.hashCode();        }    }        @EmbeddedId    private Id id = new Id();    private Date dateAdded = new Date();       @ManyToOne    @JoinColumn(name="ITEM_ID", insertable = false,updatable = false)    private Item6 item;    @ManyToOne    @JoinColumn(name="CATEGORY_ID", insertable = false, updatable = false)    private Category6 category;    } 数据库表和上面的例子类似,不再给出。   3.8    三元关联 三元关联(三个表的关联)可以采用Map实现。下面的例子建立Category类、Item类和User类的三元关联: @Entity public   class  Item7  {    @Id private int id;    private String description;} @Entity public   class  User7_3  {    @Id private int id;    private String name;} @Entity public   class  Category7  {    @Id private int id;    private String name;        @ManyToMany    @org.hibernate.annotations.MapKeyManyToMany(        joinColumns = @JoinColumn(name = "ITEM_ID"))    @JoinTable(        name = "CATEGORY7_ITEM7",         joinColumns = @JoinColumn(name = "CATEGORY_ID"),         inverseJoinColumns = @JoinColumn(name = "USER_ID")     )    private Map<Item7,User7_3> itemsAndUser = new HashMap<Item7,User7_3>();    } 数据库表如下:create table  " ITEM7 " (     " ID "  INTEGER not  null ,    " DESCRIPTION "  VARCHAR( 255 ),    constraint  " SYS_PK_2017 "  primary key ( " ID " ));create unique index  " SYS_PK_2017 "  on  " ITEM7 " ( " ID " );create table  " USER7_3 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_2028 "  primary key ( " ID " ));create unique index  " SYS_PK_2028 "  on  " USER7_3 " ( " ID " );create table  " CATEGORY7 " (     " ID "  INTEGER not  null ,    " NAME "  VARCHAR( 255 ),    constraint  " SYS_PK_1991 "  primary key ( " ID " ));create unique index  " SYS_PK_1991 "  on  " CATEGORY7 " ( " ID " );create table  " CATEGORY7_ITEM7 " (     " CATEGORY_ID "  INTEGER not  null ,    " USER_ID "  INTEGER not  null ,    " ITEM_ID "  INTEGER not  null ,    constraint  " SYS_PK_1981 "  primary key ( " CATEGORY_ID " , " ITEM_ID " ));alter table  " CATEGORY7_ITEM7 "       add constraint  " FK80A766BEA218AA4A "      foreign key ( " CATEGORY_ID " )     references  " CATEGORY7 " ( " ID " );alter table  " CATEGORY7_ITEM7 "       add constraint  " FK80A766BE4250AF20 "      foreign key ( " ITEM_ID " )     references  " ITEM7 " ( " ID " );alter table  " CATEGORY7_ITEM7 "       add constraint  " FK80A766BEE0D93124 "      foreign key ( " USER_ID " )     references  " USER7_3 " ( " ID " );create unique index  " SYS_PK_1981 "  on  " CATEGORY7_ITEM7 " ( " CATEGORY_ID " , " ITEM_ID " );create index  " SYS_IDX_2051 "  on  " CATEGORY7_ITEM7 " ( " USER_ID " );create index  " SYS_IDX_2053 "  on  " CATEGORY7_ITEM7 " ( " ITEM_ID " );create index  " SYS_IDX_2055 "  on  " CATEGORY7_ITEM7 " ( " CATEGORY_ID " );   最后,正如UML类图只是描述系统的静态结构,这里小鸡射手仅描述了ORM映射部分。如果要编写高质量的ORM程序,还需要了解实体的生命期、事务和并发、Query、Fetch机制和缓存等动态特性。 全文完。

    最新回复(0)