Hibernate入门之注解@Column详解
Jeffcky 人气:0前言
上一节我们讲解了Hibernate的主键生成策略,本节我们继续来讲讲Hibernate中针对列的映射即@Column注解,文中若有错误之处,还望指正。
@Column注解详解
我们看到如上针对列注解上所对应的属性设置,主要有列名、唯一约束(默认为非)、可空(默认为空)、可插入(默认为true)、可更新(默认为true)、列定义(默认空字符串)、所属表名(默认为空字符串)、长度(默认为255)、小数位数(默认为0)等,这里我们重点讲解insertable、updatable、columnDefinition、precision属性。
属性insertable和updatable
首先我们给出如下两个POJO对象,一个是国家、另外一个则是城市邮编,一个国家下有多个城市即对应多个邮编,而一个城市邮编则只属于特定国家,所以国家和城市邮编是一对多的关系,后续讲解关系映射时会进一步详细讲解,如下:@Entity
@Entity public class Country { public Country() { } //国家编码 @Id @Column(length = 20) private String iso_code; //国家名称 @Column private String name; public void setIso_code(String iso_code) { this.iso_code = iso_code; } public String getIso_code() { return iso_code; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToMany(mappedBy = "country", cascade = CascadeType.ALL) private List<Zip> zips; public List<Zip> getZips() { return zips; } public void setZips(List<Zip> zips) { this.zips = zips; } }
在上述国家对象中,我们以iso_code作为主键且值由我们显式指定,在如下城市邮编对象中,我们显式指定外键列名为country_code,同时呢,我们也以定义一个属性为country_code作为主键,如下:
@Entity public class Zip { //所属国家编码 @Id
@Column(length = 20) private String country_code; @ManyToOne @JoinColumn(name = "country_code") private Country country; //城市名称 @Column private String city_name; //城市邮编 @Column private String code; public void setCountry_code(String country_code) { this.country_code = country_code; } public String getCountry_code() { return country_code; } public void setCity_name(String city_name) { this.city_name = city_name; } public String getCity_name() { return city_name; } public void setCode(String code) { this.code = code; } public String getCode() { return code; } }
接下来我们打开会话去保存country,如下:
Country country = new Country(); country.setName("中国"); country.setIso_code("CHI"); Zip zip = new Zip(); zip.setCity_name("深圳"); zip.setCode("518000"); zip.setCountry_code(country.getIso_code()); country.setZips(Arrays.asList(zip)); session.save(country);
此时将抛出如上异常错误,因为我们显式指定外键列名为country_code,同时主键列名也为country_code,此时将映射为同一列,也就是说country_code属于共享主键,但是针对指定的主键列country_code,我们可以显式设定值,此时将引起外键列也为country_code即与country中的iso_code值不一致的问题,所以为了修正这种情况,Hibernate要求我们必须使用其中之一来进行插入、更新,而另外一个则将只读,所以我们需要将外键列设置为不允许插入和更新,如下:
看到网上一些文章对于上述insertable和updatable的设置将其解释为:可能我们会通过上述城市邮编对象(zip)来反向创建国家对象(country),因为zip并不负责创建和更新country,反之,我们只能通过country来创建和更新zip,其实没有很大的说服力,这个说法我个人认为是错误的,我个人认为:insertable和updatable与相关实体的插入和更新无关,此二者属性背后真正的意图是防止列在当前实体的插入和更新,也就是说在实体中多次映射字段时(比如上述共享主键),这两个属性将很有用,可以进一步进行修正,常见的场景为:使用组合键、使用共享主键、使用级联主键。
属性columnDefinition
我们知道对于字符串默认为长度为255且可空,下面我们将country对象中的name属性显示设置其长度为120且不可空,如下:
如下,我们通过属性columnDefinition来进一步设置其长度为100且不可空,此时映射到表中的列到底是可空还是不可空,长度到底是100还是120呢?
@Column(columnDefinition = "varchar(100) not null", length = 120) private String name;
我们可以看到此时将以属性columnDefinition定义的为准,也就是length将会被覆盖,那是不是说明通过columnDefinition设置后都将会被覆盖,事实真的如此吗?
@Column(columnDefinition = "varchar(100) null", nullable = false, length = 120) private String name;
create table Country (iso_code varchar(20) not null, name varchar(100) null not null, primary key (iso_code))
我们可以看到此时通过columnDefinition属性上设置可空,但是通过nullable设置为不可空,最终映射到表中的列却是不可空,这说明此时columnDefinition中的null被冗余。我们可以看到我们设置长度、可空、精度、唯一约束有两种方式,其一可以通过对应属性比如length、nullable、precison、unique设置,其二可以通过columnDefinition设置,那么为何Hibernate要同时提供这两种方式呢?它的目的是什么呢?我认为提供这两种方式说明了其灵活性,若是简单的设置(比如只设置长度)则直接使用length即可,若需全部设置,则通过columnDefinition设置来的方便。数据库DDL由:(name + columnDefinition)构成,它是物理的,即columnDefinition是生成列的DDL时使用的SQL片段,对于长度、精度、唯一约束使用columnDefinition将会被覆盖,而对于null可能被覆盖或冗余。
总结
本节我们详细讲解了列注解@Column上的属性insertable和updatable以及columnDefinition的详细使用,下一节我们讲解枚举注解,感谢您的阅读,我们下节见。
加载全部内容