模型驱动
可以通过模型驱动实现快速开发,所谓模型驱动是指先开发模型类和映射文件,数据库表结构由此生成;此外还有数据驱动,是指根据数据库表结构生成模型类和映射文件。模型类是实体类的另一种叫法。Hibernate允许调用SchemaExport/SchemaUpdate将根据实体类映射关系将DDL语句输出到控制台、数据库、文件系统等。
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.configure()
.build();
Metadata metadata = new MetadataSources(serviceRegistry).buildMetadata();
SchemaExport schemaExport = new SchemaExport();
//设置schemaExport
...
schemaExport.create(EnumSet.of(输出类型), metadata);
以上的输出类型分为:数据库、文件系统、标准控制台。其中在输出到文件系统的时候需要用户设置输出的文件路径。
schemaExport.setOutputFile("/Users/admin/IdeaProjects/20200430/HibernateORM/export.sql");
schemaExport.create(EnumSet.of(TargetType.SCRIPT), metadata);
当输出到标准控制台的时候,用户可以设置输出格式
schemaExport.setFormat(true);
schemaExport.create(EnumSet.of(TargetType.STDOUT), metadata);
当用户指定类型是数据库的时候则输出脚本到数据库
schemaExport.create(EnumSet.of(TargetType.DATABASE), metadata);
用户可以调用drop方法清除数据库中的表
schemaExport.drop(EnumSet.of(TargetType.DATABASE), metadata);
Hibernate允许调用SchemaUpdate将根据实体类映射关系将DDL语句数据库,如果数据库中已经存在表则修改该表,如果表不存在则创建该表
SchemaUpdate schemaUpdate = new SchemaUpdate();
schemaUpdate.setFormat(true);
schemaUpdate.execute(EnumSet.of(TargetType.DATABASE), metadata);
主键映射
Hibernate提供了基于不同数据库的主键自动生成策略,主要有assigned、uuid、increment、
identity、sequence、seqhilo以及native的实现策略。下面我们分别研究一下Hibernate主键的生成策略。
assigned
由应用程序给主键赋值,例如将用户名作为主键时,用户名应当由程序指定而非数据库。如果在保存实体类的时候,用户不去设置id主键,则系统忽略保存行为。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="assigned"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
uuid
由hibernate生成一个uuid值作为主键,主键类型是字符类型,长度必须为32
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略,必须是字符串 -->
<generator class="assigned"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
increment
在现有记录的id最大值基础上加1作为下一条新增记录的主键值,因为会有并发问题,只适合测试。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="increment"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
identity
必须配合支持自增长列的数据库使用,如: MS SQL Server、MySQL等
<class name="User" table="t_user" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="identity"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
sequence
使用序列生成主键,一般配合支持序列的数据库使用,如:Oracle、DB2、PostgreSql等
<class name="User" table="t_user" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="sequence">
<param name="sequence">user_seq</param>
</generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
seqhilo
使用序列生成高位值,hibernate生成低位值。由于减少的查询序列的次数,性能较直接使用sequence主键生成方式高,一般配合支持序列的数据库使用,如:Oracle、DB2、PostgreSql等。使用序列生成高位值,hibernate生成低位值。高低位计算伪代码:
if(lo > max_lo ) {
lastSequenceValue= 从数据库获取
lo = 0;
hi = lastSequenceValue * ( max_lo + 1 );
}
id = hi + lo++ ;
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="seqhilo">
<param name="sequence">t_user_seq</param>
<param name="max_lo">10</param>
</generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
native
由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
<class name="User" table="t_user" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
foreign
使用另外一个相关联的对象的主键作为该对象主键。主要用于一对一关系中。
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
<one-to-one name="user" class="User" constrained="true" />
</class>
关系映射
关联映射通常是最难正确实现的事情。在本节中,我们将逐一检查一些典型案例,首先是单向映射,然后是双向案例。在传统的数据建模中,可空外键不被认为是一种好习惯,因此我们的示例未使用可空外键。这不是Hibernate的要求,如果您删除了可空性约束,则映射将起作用。
单向关联
Many-to-one
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
<class name="Order" table="t_order" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="uuid"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="cost" column="cost" type="double" />
<property name="createTime" column="create_time" type="date" />
<many-to-one name="user" column="user_id" foreign-key="user_id"/>
</class>
create table baizhi.t_order (id varchar(255) not null, name varchar(255), cost double precision, create_time date, user_id integer not null, primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
alter table baizhi.t_order add constraint user_id foreign key (user_id) references baizhi.t_user (id)
One-to-one
外键上的单向一对一关联几乎相同。唯一的区别是列唯一约束。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<many-to-one name="idCard" column="card_id" unique="true" not-null="true" />
</class>
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
</class>
create table baizhi.t_cart (id integer not null auto_increment, card_no varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, card_id integer not null, primary key (id)) engine=InnoDB
alter table baizhi.t_user add constraint UK_qnoc8aliry5wdc6glg35gw9m8 unique (card_id)
alter table baizhi.t_user add constraint FK1f3yefcrkfl681hx0dihhkjfu foreign key (card_id) references baizhi.t_cart (id)
主键上的单向一对一关联通常使用特殊的id生成器,但是在此示例中,我们使用了共享主键方式。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
<one-to-one name="user" constrained="true" />
</class>
create table baizhi.t_cart (id integer not null, card_no varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
alter table baizhi.t_cart add constraint FK54i7bflxomtnplgv015ii66r1 foreign key (id) references baizhi.t_user (id)
One-to-many
外键上的单向一对多关联是一种不常见的情况,因此不建议这样做。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<set name="orders" >
<key column="user_id"/>
<one-to-many class="Order" />
</set>
</class>
<class name="Order" table="t_order" catalog="baizhi">
<id name="id" column="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="cost" column="cost" type="double" />
<property name="createTime" column="create_time" type="date" />
</class>
create table baizhi.t_order (id varchar(255) not null, name varchar(255), cost double precision, create_time date, user_id integer, primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
alter table baizhi.t_order add constraint FKho2r4qgj3txpy8964fnla95ub foreign key (user_id) references baizhi.t_user (id)
单向关联-JoinTable
Many-to-one
当关联表是可选的时,联接表上的单向多对一关联是常见的。例如:
<class name="Order" table="t_order" catalog="baizhi">
<id name="id" column="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="cost" column="cost" type="double" />
<property name="createTime" column="create_time" type="date" />
<join table="t_order_user">
<key column="order_id" unique="true" ></key>
<many-to-one name="user" column="user_id" not-null="true"></many-to-one>
</join>
</class>
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
</class>
create table baizhi.t_order (id varchar(255) not null, name varchar(255), cost double precision, create_time date, primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
create table t_order_user (order_id varchar(255) not null, user_id integer not null, primary key (order_id)) engine=InnoDB
alter table t_order_user add constraint FKjvd2fh1hj6ka7pym3b7s2srup foreign key (order_id) references baizhi.t_order (id)
alter table t_order_user add constraint FKfrbe6oahvvlpchcbx4at7movp foreign key (user_id) references baizhi.t_user (id)
One-to-one
联接表上的单向一对一关联是可能的,但是非常少见。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<join table="t_user_card" optional="true">
<key column="user_id" unique="true" not-null="true"/>
<many-to-one name="idCard" column="card_id" unique="true" not-null="true"/>
</join>
</class>
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
</class>
create table baizhi.t_cart (id integer not null, card_no varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
create table t_user_card (user_id integer not null, card_id integer not null, primary key (user_id)) engine=InnoDB
alter table t_user_card add constraint UK_iy6k1jl628vvfx6r03jh2p2cl unique (card_id)
alter table t_user_card add constraint FKiut1yjvicgjsnf5s5wfi227pe foreign key (user_id) references baizhi.t_user (id)
alter table t_user_card add constraint FKe1g1so1y3w9ev3d55l1xqgxvt foreign key (card_id) references baizhi.t_cart (id)
One-to-many
联接表上的单向一对多关联是首选。指定unique =“ true”,将多重性从多对多更改为一对多。
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<set name="orders" table="t_user_order">
<key column="user_id"></key>
<many-to-many column="order_id" unique="true" class="Order" />
</set>
</class>
<class name="Order" table="t_order" catalog="baizhi">
<id name="id" column="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="cost" column="cost" type="double" />
<property name="createTime" column="create_time" type="date" />
</class>
create table baizhi.t_order (id varchar(255) not null, name varchar(255), cost double precision, create_time date, primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
create table t_user_order (user_id integer not null, order_id varchar(255) not null, primary key (user_id, order_id)) engine=InnoDB
alter table t_user_order add constraint UK_91yyb7t48vww46t0phj3a59nb unique (order_id)
alter table t_user_order add constraint FKnaawxjqatam4xa20uo0e3q7n3 foreign key (order_id) references baizhi.t_order (id)
alter table t_user_order add constraint FKtpjow4b909xk4i0yqwow67xe1 foreign key (user_id) references baizhi.t_user (id)
Many-to-many
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<set name="tags" table="t_user_tag">
<key column="user_id"></key>
<many-to-many column="tag_id" class="Tag" />
</set>
</class>
<class name="com.baizhi.Tag" table="t_tag" catalog="baizhi">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string" length="128"/>
</class>
create table baizhi.t_tag (id integer not null auto_increment, name varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
create table t_user_tag (user_id integer not null, tag_id integer not null, primary key (user_id, tag_id)) engine=InnoDB
alter table t_user_tag add constraint FK1eehkuwvoy0l06l6y8mg88uca foreign key (tag_id) references baizhi.t_tag (id)
alter table t_user_tag add constraint FKn4s08743ufgbccuf7odhx2eiu foreign key (user_id) references baizhi.t_user (id)
双向关联
Many-to-one/One to Many
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<set name="orders" >
<key column="user_id"/>
<one-to-many class="Order" />
</set>
</class>
<class name="Order" table="t_order" catalog="baizhi">
<id name="id" column="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="cost" column="cost" type="double" />
<property name="createTime" column="create_time" type="date" />
<many-to-one name="user" column="user_id" not-null="true" />
</class>
create table baizhi.t_order (id varchar(255) not null, name varchar(255), cost double precision, create_time date, user_id integer not null, primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
alter table baizhi.t_order add constraint FKho2r4qgj3txpy8964fnla95ub foreign key (user_id) references baizhi.t_user (id)
One-to-one
方案1:唯一外键
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<many-to-one name="idCard" column="card_id" unique="true" not-null="true" />
</class>
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
<one-to-one name="user" property-ref="idCard"/>
</class>
create table baizhi.t_cart (id integer not null, card_no varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, card_id integer not null, primary key (id)) engine=InnoDB
alter table baizhi.t_user add constraint UK_qnoc8aliry5wdc6glg35gw9m8 unique (card_id)
alter table baizhi.t_user add constraint FK1f3yefcrkfl681hx0dihhkjfu foreign key (card_id) references baizhi.t_cart (id)
方案2:共享主键关联
<class name="com.baizhi.IdCard" table="t_cart" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
<property name="cardNo" column="card_no" type="string" length="128"/>
<one-to-one name="user" constrained="true" />
</class>
</hibernate-map
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<one-to-one name="idCard" />
</class>
create table baizhi.t_cart (id integer not null, card_no varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
alter table baizhi.t_cart add constraint FK54i7bflxomtnplgv015ii66r1 foreign key (id) references baizhi.t_user (id)
Many-to-many
<class name="com.baizhi.Tag" table="t_tag" catalog="baizhi">
<!-- class下必须要有一个id的子元素,id是用于描述主键的 -->
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string" length="128"/>
<set name="users" table="t_user_tag">
<key column="tag_id"></key>
<many-to-many column="user_id" class="User" />
</set>
</class>
<class name="User" table="t_user" catalog="baizhi">
<id name="id" column="id">
<!-- 主键生成策略 -->
<generator class="native"></generator>
</id>
<property name="name" column="name" length="255"/>
<property name="age" column="age" type="int" length="8" />
<property name="birthDay" column="birth_day" type="date" />
<property name="salary" column="salary" type="double" />
<set name="tags" table="t_user_tag">
<key column="user_id"></key>
<many-to-many column="tag_id" class="Tag" />
</set>
</class>
create table baizhi.t_tag (id integer not null auto_increment, name varchar(128), primary key (id)) engine=InnoDB
create table baizhi.t_user (id integer not null auto_increment, name varchar(255), age integer, birth_day date, salary double precision, primary key (id)) engine=InnoDB
create table t_user_tag (user_id integer not null, tag_id integer not null, primary key (tag_id, user_id)) engine=InnoDB
alter table t_user_tag add constraint FK1eehkuwvoy0l06l6y8mg88uca foreign key (tag_id) references baizhi.t_tag (id)
alter table t_user_tag add constraint FKn4s08743ufgbccuf7odhx2eiu foreign key (user_id) references baizhi.t_user (id)