Hibernate注解开发、注解创建索引
1.注解的目的
简化繁琐的ORM映射文件(*.hbm)的配置
2.JPA和hibernate的关系
JPA:java persistence API,JPA注解是JavaEE的标准和规范。
两者的关系可以简单理解为JPA是接口,Hibernate是实现,但是其功能是JPA的超集。
Hibernate如何实现与JPA的关系?
通过hibernate-core,hibernate-entitymanager,hibernate-annotation三个组件实现。
程序开发中一般使用JPA注解,便于程序的扩展和移植。
3.Hibernate注解分类:
类级别注解
@Entity 表示实体类,对应DB中一张表
@Table 表示DB中的表
@Embeddable 嵌入类
属性级别注解
映射关系注解
4.Hibernate注解的使用
1.导包与准备工具类:
pom包依赖:
<dependencies> <!-- slf4j 依赖包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.0-rc1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.0-rc1</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.0.7.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency> </dependencies>
工具类:
package cn.qlq.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.service.ServiceRegistry; public class HibernateUtil { private static SessionFactory sessionFactory; // 创建一个对象,一个web项目只有一个SessionFactory static { // 3.3以及之前的版本构建会话工厂对象 // SessionFactory sessionFactory = new // Configuration().configure().buildSessionFactory(); // 5.0之后获取SessionFactory // 创建服务注册对象 ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().configure().build(); // 创建会话工厂对象 sessionFactory = new MetadataSources(serviceRegistry).buildMetadata().buildSessionFactory(); } // 获得session => 获得全新session public static Session openSession() { return sessionFactory.openSession(); } // 获得session => 获得与线程绑定的session public static Session getCurrentSession() { return sessionFactory.getCurrentSession(); } }
5.类注解的使用
5.1@Entity注解的使用---属于javax包
@Entity:映射实体类、
@Entity(name = "tableName")
name是可选属性,指定数据库的表名,如果不写的话默认与实体类名相同。
注意:使用@Entity必须指定实体的主键属性(可以在get方法上设置,也可以直接在属性设置)
package cn.qlq.domain; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; @Entity(name = "t_student") // 注意包名是javax public class Student { // @Id private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private String address; @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
hibernate.cfg.xml配置注解实体类:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property> <property name="hibernate.connection.username">sa</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 使用二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!--设置缓存的类型,设置缓存的提供商 --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property> <!-- #hibernate.show_sql true #hibernate.format_sql true --> <!-- 将hibernate生成的sql语句打印到控制台 --> <property name="hibernate.show_sql">true</property> <!-- 将hibernate生成的sql语句格式化(语法缩进) --> <property name="hibernate.format_sql">true</property> <!-- ## auto schema export 自动导出表结构. 自动建表 #hibernate.hbm2ddl.auto create 自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用) #hibernate.hbm2ddl.auto create-drop 自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用) #hibernate.hbm2ddl.auto update(推荐使用) 自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据). #hibernate.hbm2ddl.auto validate 校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败. --> <property name="hibernate.hbm2ddl.auto">create</property> <!-- 引入orm注解类 --> <mapping class="cn.qlq.domain.Student" /> </session-factory> </hibernate-configuration>
测试:
package cn.qlq.test; import org.junit.Test; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { HibernateUtil.openSession(); } }
日志:
Hibernate: drop table if exists t_student 2018-08-27 23:12:02 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Checking for update... Hibernate: create table t_student ( id integer not null, address varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (id) )
SQL表:(主键没有自增)
mysql> desc t_student; +----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | address | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | name | varchar(255) | YES | | NULL | | | sex | char(1) | YES | | NULL | | +----------+--------------+------+-----+---------+-------+
5.2@Table注解
@Table(name="xxx",catalog="xxx",schema="xxx")
@Entity配合使用,只能标注在实体的class处定义,表示实体对应的数据库表的信息
name:可选映射表的名称,不写的话与类名称相同
catalog:可选(目录名称),表示Catalog名称,默认为Catalog("")
schema:可选的模式名称,表示Scheme名称,默认为Scheme("")
Catalog与Schema解释:
自己的测试:
package cn.qlq.domain; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu", schema = "hibernate") // 注意包名是javax public class Student { // @Id private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private String address; @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
SQL:
Hibernate: drop table if exists t_stu 2018-09-06 21:50:43 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Checking for update... 2018-09-06 21:50:44 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Update check failed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?kitID=ehcache.default&pageID=update.properties&id=-1062731519&os-name=Windows+8.1&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.7.0_80&platform=amd64&tc-version=UNKNOWN&tc-product=Ehcache+Core+2.4.3&source=Ehcache+Core&uptime-secs=1&patch=UNKNOWN Hibernate: create table t_stu ( id integer not null, address varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (id) )
5.3@Embeddable表示此类一个嵌入类,经常作为另一个类的成员属性,在生成数据库表的时候该类的成员会作为其主类的属性添加到数据库
例如如下Address是一个嵌入类:
package cn.qlq.domain; import javax.persistence.Embeddable; @Embeddable /** 表示此类是一个嵌入类,作为其他类 的成员属性 **/ public class Address { private int addreCode; private String addressName; public int getAddreCode() { return addreCode; } public void setAddreCode(int addreCode) { this.addreCode = addreCode; } public String getAddressName() { return addressName; } public void setAddressName(String addressName) { this.addressName = addressName; } }
package cn.qlq.domain; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu", schema = "hibernate") // 注意包名是javax public class Student { // @Id private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private Address address; @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
发出的SQL:
create table t_stu ( id integer not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (id) )
6.属性级别的注解:
使用方法有两种:第一种可以放在属性的头顶,第二种可以在属性的getter方法前面。
主要有以下注解(红色是重要的):
@Id,@SequenceGenerator,@GeneratedValue,@Column,@Embedded,@EmbeddedId,@Lob,@Version,@Basic,@Transient
1.@Id注解(必须有)
定义了映射到数据库表的主键的属性,一个实体类可以有一个或者多个属性被映射为主键,如果是多个属性为主键属性,实体必须实现Serializable接口。而且String类型的ID长度不能太长,默认长度是255(超过允许的主键长度),所以需要结合@Column指定列的长度。
例如:(注意注解放的位置一致,都放在getter或者属性前面)
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu", schema = "hibernate") // 注意包名是javax public class Student implements Serializable { private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private Address address; @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Id @Column(length = 8) public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
SQL:
drop table if exists t_stu create table t_stu ( name varchar(8) not null, id integer not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, sex char(1), primary key (name, id) )
mysql> desc t_stu; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | name | varchar(8) | NO | PRI | NULL | | | id | int(11) | NO | PRI | NULL | | | addreCode | int(11) | NO | | NULL | | | addressName | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | sex | char(1) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 7 rows in set (0.01 sec)
2.GeneratedValue注解---定义注解生成策略
如下测试都是基于mysql数据库:
- 测试int类型(Hibernate)
Auto是利用一个表生成全局唯一ID。查询的时候利用for udate锁住表防止生成重复的ID,这也是生成全局唯一ID的思路。
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu", schema = "hibernate") // 注意包名是javax public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) // 等价于@GeneratedValue,根据底层数据库选择。因为数据库是mysql,5.0之后是利用表生成全局唯一ID private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
测试类:
package cn.qlq.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.domain.Address; import cn.qlq.domain.Student; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Address a = new Address(); a.setAddressName("地球"); Student st = new Student(); st.setAddress(a); session.save(st); tx.commit(); session.close(); } }
SQL:
Hibernate: drop table if exists hibernate_sequence 2018-09-06 22:57:34 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Checking for update... Hibernate: drop table if exists t_stu 2018-09-06 22:57:34 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Update check failed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?kitID=ehcache.default&pageID=update.properties&id=-1062731519&os-name=Windows+8.1&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.7.0_80&platform=amd64&tc-version=UNKNOWN&tc-product=Ehcache+Core+2.4.3&source=Ehcache+Core&uptime-secs=1&patch=UNKNOWN Hibernate: create table hibernate_sequence ( next_val bigint ) Hibernate: insert into hibernate_sequence values ( 1 ) Hibernate: create table t_stu ( id integer not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (id) ) Hibernate: select next_val as id_val from hibernate_sequence for update Hibernate: update hibernate_sequence set next_val= ? where next_val=? Hibernate: insert into t_stu (addreCode, addressName, age, birthDay, name, sex, id) values (?, ?, ?, ?, ?, ?, ?)
总结:
hibernate5.0之后是新建一个数据表生成全局唯一的ID,每次查询的时候利用update语句去锁住表然后查询到ID,然后将表的值加一,并将取出的ID使用上,这也是生成全局唯一ID的一种思路。
mysql> select * from hibernate_sequence; +----------+ | next_val | +----------+ | 2 | +----------+ 1 row in set (0.00 sec) mysql> select * from t_stu; +----+-----------+-------------+------+----------+------+------+ | id | addreCode | addressName | age | birthDay | name | sex | +----+-----------+-------------+------+----------+------+------+ | 1 | 0 | 地球 | NULL | NULL | NULL | NULL | +----+-----------+-------------+------+----------+------+------+ 1 row in set (0.06 sec)
我们将int型设为自增:
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer age; private Date birthDay; private Character sex; private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
测试类:
package cn.qlq.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.domain.Address; import cn.qlq.domain.Student; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Address a = new Address(); a.setAddressName("地球"); Student st = new Student(); st.setAddress(a); session.save(st); tx.commit(); session.close(); } }
SQL:
Hibernate: drop table if exists t_stu 2018-09-06 23:05:19 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Checking for update... Hibernate: create table t_stu ( id integer not null auto_increment, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (id) ) 2018-09-06 23:05:20 [net.sf.ehcache.util.UpdateChecker]-[DEBUG] Update check failed: java.io.IOException: Server returned HTTP response code: 403 for URL: http://www.terracotta.org/kit/reflector?kitID=ehcache.default&pageID=update.properties&id=-1062731519&os-name=Windows+8.1&jvm-name=Java+HotSpot%28TM%29+64-Bit+Server+VM&jvm-version=1.7.0_80&platform=amd64&tc-version=UNKNOWN&tc-product=Ehcache+Core+2.4.3&source=Ehcache+Core&uptime-secs=1&patch=UNKNOWN Hibernate: insert into t_stu (addreCode, addressName, age, birthDay, name, sex) values (?, ?, ?, ?, ?, ?)
mysql> desc t_stu; +-------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | addreCode | int(11) | NO | | NULL | | | addressName | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | name | varchar(255) | YES | | NULL | | | sex | char(1) | YES | | NULL | | +-------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
- 测试String类型的UUID生成值与手动设置值
(1)string类型做主键手动设置值:(需要结合hibernate的主键生成器)
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { private Integer id; @Id @GeneratedValue(generator = "sid") // 指定生成器d名字 @GenericGenerator(name = "sid", strategy = "assigned") // hibernate的生成器,name必须与上面一样 @Column(length = 40) private String name; private Integer age; private Date birthDay; private Character sex; private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
测试类:
package cn.qlq.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.domain.Address; import cn.qlq.domain.Student; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Address a = new Address(); a.setAddressName("地球"); Student st = new Student(); st.setAddress(a); st.setName("张三"); session.save(st); tx.commit(); session.close(); } }
SQL:
Hibernate: drop table if exists t_stu create table t_stu ( name varchar(40) not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, id integer, sex char(1), primary key (name) ) insert into t_stu (addreCode, addressName, age, birthDay, id, sex, name) values (?, ?, ?, ?, ?, ?, ?)
mysql> desc t_stu; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | name | varchar(40) | NO | PRI | NULL | | | addreCode | int(11) | NO | | NULL | | | addressName | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | id | int(11) | YES | | NULL | | | sex | char(1) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 7 rows in set (0.00 sec) mysql> select * from t_stu; +------+-----------+-------------+------+----------+------+------+ | name | addreCode | addressName | age | birthDay | id | sex | +------+-----------+-------------+------+----------+------+------+ | 张三 | 0 | 地球 | NULL | NULL | NULL | NULL | +------+-----------+-------------+------+----------+------+------+ 1 row in set (0.07 sec)
(2)string类型做主键UUID生成值:(需要结合hibernate的主键生成器)
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { private Integer id; @Id @GeneratedValue(generator = "uid") // 指定生成器d名字 @GenericGenerator(name = "uid", strategy = "uuid") // hibernate的生成器,name必须与上面一样 @Column(length = 40) private String name; private Integer age; private Date birthDay; private Character sex; private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
测试类:
package cn.qlq.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.domain.Address; import cn.qlq.domain.Student; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Address a = new Address(); a.setAddressName("地球"); Student st = new Student(); st.setAddress(a); session.save(st); tx.commit(); session.close(); } }
SQL:
drop table if exists t_stu create table t_stu ( name varchar(40) not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, id integer, sex char(1), primary key (name) ) insert into t_stu (addreCode, addressName, age, birthDay, id, sex, name) values (?, ?, ?, ?, ?, ?, ?)
mysql> desc t_stu; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | name | varchar(40) | NO | PRI | NULL | | | addreCode | int(11) | NO | | NULL | | | addressName | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | id | int(11) | YES | | NULL | | | sex | char(1) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 7 rows in set (0.00 sec) mysql> select * from t_stu; +----------------------------------+-----------+-------------+------+---------- | name | addreCode | addressName | age | birthDay +----------------------------------+-----------+-------------+------+---------- | 4028818165af748f0165af749da50000 | 0 | 地球 | NULL | NULL +----------------------------------+-----------+-------------+------+---------- 1 row in set (0.00 sec)
3.@Column注解(简单但是重要)
可将属性映射到列,使用该注解来覆盖默认值,@Column描述了数据库表中该字段的详细定义,这对于根据JPA注解生成数据库表结构的工具非常有作用。
常用属性:
name:可选,表示数据库表中该字段的名称,默认与属性名称一致。
nullable:可选,表示该字段是否允许为null,默认为true
unique:可选表示该字段是否唯一,默认false。
length:可选,表示该字段的大小,仅对String类型的字段有效,默认值是255.(如果是主键不能使用默认值)
insertable:可选,表示在ORM框架执行插入操作时,该字段是否应该出现在INSERT语句中,默认为true
updateable:可选,表示在ORM框架执行更新操作时,该字段是否应该出现在update语句中,默认为true。对于一经创建就不可以更改的字段,该属性非常有用,比如birthday字段。
4.@Embedded属性注解:
表示该成员属性是一个嵌入类。在其类上面也要有注解@Embeddable表示此类是一个嵌入类,作为其他类的成员属性,否则会报错。如下:
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { private Integer id; @Id @GeneratedValue(generator = "uid") // 指定生成器d名字 @GenericGenerator(name = "uid", strategy = "uuid") // hibernate的生成器,name必须与上面一样 @Column(length = 40) private String name; private Integer age; private Date birthDay; private Character sex; @Embedded private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
package cn.qlq.domain; import javax.persistence.Embeddable; @Embeddable /** 表示此类是一个嵌入类,作为其他类 的成员属性 **/ public class Address { private int addreCode; private String addressName; public int getAddreCode() { return addreCode; } public void setAddreCode(int addreCode) { this.addreCode = addreCode; } public String getAddressName() { return addressName; } public void setAddressName(String addressName) { this.addressName = addressName; } }
5.@EmbeddedId属性注解:
使用嵌入式主键类实现复合主键。
注意:嵌入式主键类必须实现Serializable接口、必须有默认的public无参构造方法、必须重写Object父类的equals方法和hashCode方法。
例如:
package cn.qlq.domain; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Embeddable public class StudentPK implements Serializable { /** * */ private static final long serialVersionUID = 1690932463714731211L; @Column(length = 40) private String idCode;// 身份证号 @Column(length = 40) private String stuNum;// 学号 public String getIdCode() { return idCode; } public void setIdCode(String idCode) { this.idCode = idCode; } public String getStuNum() { return stuNum; } public void setStuNum(String stuNum) { this.stuNum = stuNum; } public StudentPK() { } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } }
package cn.qlq.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.Embedded; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { @EmbeddedId private StudentPK studentPK; private String name; private Integer age; private Date birthDay; private Character sex; @Embedded private Address address; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public StudentPK getStudentPK() { return studentPK; } public void setStudentPK(StudentPK studentPK) { this.studentPK = studentPK; } }
测试类:
package cn.qlq.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.domain.Address; import cn.qlq.domain.Student; import cn.qlq.domain.StudentPK; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Address a = new Address(); a.setAddressName("地球"); StudentPK sp = new StudentPK(); sp.setIdCode("123456789987654321"); sp.setStuNum("s001"); Student st = new Student(); st.setAddress(a); st.setStudentPK(sp); session.save(st); tx.commit(); session.close(); } }
SQL:
drop table if exists t_stu create table t_stu ( idCode varchar(40) not null, stuNum varchar(40) not null, addreCode integer not null, addressName varchar(255), age integer, birthDay datetime, name varchar(255), sex char(1), primary key (idCode, stuNum) ) insert into t_stu (addreCode, addressName, age, birthDay, name, sex, idCode, stuNum) values (?, ?, ?, ?, ?, ?, ?, ?)
mysql> desc t_stu; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | idCode | varchar(40) | NO | PRI | NULL | | | stuNum | varchar(40) | NO | PRI | NULL | | | addreCode | int(11) | NO | | NULL | | | addressName | varchar(255) | YES | | NULL | | | age | int(11) | YES | | NULL | | | birthDay | datetime | YES | | NULL | | | name | varchar(255) | YES | | NULL | | | sex | char(1) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 8 rows in set (0.00 sec) mysql> select * from t_stu\G *************************** 1. row *************************** idCode: 123456789987654321 stuNum: s001 addreCode: 0 addressName: 地球 age: NULL birthDay: NULL name: NULL sex: NULL 1 row in set (0.00 sec)
6.@Transient注解
表示该属性不会映射到数据库中,ORM框架会忽略该属性,如果一个属性不是数据库的映射必须添加这个注解,否则ORM框架会自动将其注解为@Basic.
import java.io.Serializable; import java.util.Date; import javax.persistence.Embedded; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; //@Entity(name = "t_student") // 注意包名是javax @Entity @Table(name = "t_stu") // 注意包名是javax public class Student implements Serializable { @EmbeddedId private StudentPK studentPK; private String name; private Integer age; private Date birthDay; private Character sex; @Transient private Double salary; @Embedded private Address address; .... }
7.@Lob注解
映射为mysql的longtext类型和oracle的clob类型。而且Lob字段不能添加索引.我们直接在数据库对text字段添加索引会报错:
BLOB/TEXT column 'description' used in key specification without a key length
如下代码只创字段不加索引:
@Lob @Index(name="description") private String description;
7.关联关系级别的注解
实体之间的映射关系:
一对一:一个人一个身份证号
一对多(多对一):一个人与银行账号
多对多:一个学生有多个老师,一个老师教多个学生。
1.一对一单向外键关联(一个人对一个身份证号)
两个重要的注解:
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "IdCardPid", unique = true, referencedColumnName="pid")
name:在本表中外键的名称,unique是否唯一,referencedColumnName外键列(默认是另一个实体的主键),columnDefinition:列类型的定义
例如:
package cn.qlq.AnnotationRelation.One2One; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import org.hibernate.annotations.Cascade; @Entity public class Student implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Integer age; private Character sex; private Double salary; // 关联的身份证 @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "IdCardPid", unique = true, referencedColumnName = "pid") private IdCard idCard; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public IdCard getIdCard() { return idCard; } public void setIdCard(IdCard idCard) { this.idCard = idCard; } }
package cn.qlq.AnnotationRelation.One2One; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; @Entity public class IdCard { @Id @Column(length = 40) @GeneratedValue(generator = "uid") @GenericGenerator(name = "uid", strategy = "uuid") private String pid; private String sname; private Date birthDay; public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } }
测试:
package cn.qlq.test; import java.util.Date; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import cn.qlq.AnnotationRelation.One2One.IdCard; import cn.qlq.AnnotationRelation.One2One.Student; import cn.qlq.util.HibernateUtil; public class TestEntityAnno { @Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st = new Student(); st.setSalary(7500d); IdCard idCard = new IdCard(); idCard.setSname("qlq"); idCard.setBirthDay(new Date()); st.setIdCard(idCard); session.save(idCard); session.save(st); tx.commit(); session.close(); } }
SQL:
alter table Student drop foreign key FKo88r74j7fjtuhwfra81068y61 drop table if exists IdCard drop table if exists Student create table IdCard ( pid varchar(40) not null, birthDay datetime, sname varchar(255), primary key (pid) ) create table Student ( id bigint not null auto_increment, age integer, salary double precision, sex char(1), IdCardPid varchar(40), primary key (id) ) alter table Student add constraint UK_pn1nraxgy15mgjcvs474bl5bd unique (IdCardPid) alter table Student add constraint FKo88r74j7fjtuhwfra81068y61 foreign key (IdCardPid) references IdCard (pid) insert into IdCard (birthDay, sname, pid) values (?, ?, ?) insert into Student (age, IdCardPid, salary, sex) values (?, ?, ?, ?)
结果:
mysql> desc student; +-----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | | IdCardPid | varchar(40) | YES | UNI | NULL | | +-----------+-------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) mysql> desc idcard; +----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+-------+ | pid | varchar(40) | NO | PRI | NULL | | | birthDay | datetime | YES | | NULL | | | sname | varchar(255) | YES | | NULL | | +----------+--------------+------+-----+---------+-------+ 3 rows in set (0.01 sec) mysql> select * from student; +----+------+--------+------+----------------------------------+ | id | age | salary | sex | IdCardPid | +----+------+--------+------+----------------------------------+ | 1 | NULL | 7500 | NULL | 4028818165b73b770165b73b8ffe0000 | +----+------+--------+------+----------------------------------+ 1 row in set (0.00 sec) mysql> select * from idcard; +----------------------------------+---------------------+-------+ | pid | birthDay | sname | +----------------------------------+---------------------+-------+ | 4028818165b73b770165b73b8ffe0000 | 2018-09-08 11:30:59 | qlq | +----------------------------------+---------------------+-------+ 1 row in set (0.00 sec)
2.一对一对双向外键关联 (一方维护关系,另一方放弃维护关系)
主控方的配置与上面一样,只是被控方增加一个配置。(Student是主控方,IdCard是被控方)
@OneToOne(mappedBy = "idCard")
必须设置mapperedBy属性。因为双向关联只能交给一方去控制,不可能在双方都保存外键关系,否则会导致双方都保存不进去。
如下:
Student.java与上面一样:
IdCard.java修改配置:
package cn.qlq.AnnotationRelation.One2One; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import org.hibernate.annotations.GenericGenerator; @Entity public class IdCard { @Id @Column(length = 40) @GeneratedValue(generator = "uid") @GenericGenerator(name = "uid", strategy = "uuid") private String pid; private String sname; private Date birthDay; @OneToOne(mappedBy = "idCard") private Student student; public String getPid() { return pid; } public void setPid(String pid) { this.pid = pid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } @Override public String toString() { return "IdCard [pid=" + pid + ", sname=" + sname + ", birthDay=" + birthDay + ", student=" + student + "]"; } }
上面SQL生成的与上面的一样,不会再IdCard表中维护Student的外键,否则会导致双方都保存不进去。
测试查询SQL:
@Test public void test2() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); String hql = "from IdCard"; Query query = session.createQuery(hql); List list = query.list(); System.out.println(list); tx.commit(); session.close(); }
结果:(会自动映射Student信息到IDCard类中)
Hibernate: select idcard0_.pid as pid1_0_, idcard0_.birthDay as birthDay2_0_, idcard0_.sname as sname3_0_ from IdCard idcard0_ Hibernate: select student0_.id as id1_1_1_, student0_.age as age2_1_1_, student0_.IdCardPid as IdCardPi5_1_1_, student0_.salary as salary3_1_1_, student0_.sex as sex4_1_1_, idcard1_.pid as pid1_0_0_, idcard1_.birthDay as birthDay2_0_0_, idcard1_.sname as sname3_0_0_ from Student student0_ left outer join IdCard idcard1_ on student0_.IdCardPid=idcard1_.pid where student0_.IdCardPid=?
3.多对一单向外键关联
多方持有一方的引用,比如多个学生对一个班级,学生中有班级的引用。例如:
Student.java
package cn.qlq.AnnotationRelation.ManyToOne; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Entity public class Student implements Serializable { /** * */ private static final long serialVersionUID = -624145366487777133L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Integer age; private Character sex; private Double salary; @ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER) @JoinColumn(name = "classId") private ClassRoom classRoom; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Character getSex() { return sex; } public void setSex(Character sex) { this.sex = sex; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public ClassRoom getClassRoom() { return classRoom; } public void setClassRoom(ClassRoom classRoom) { this.classRoom = classRoom; } }
ClssRoom.class
package cn.qlq.AnnotationRelation.ManyToOne; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import org.hibernate.annotations.GenericGenerator; /** * 班级类 * * @author liqiang * */ @Entity public class ClassRoom { @Id @Column(length = 40) @GeneratedValue(generator = "uid") @GenericGenerator(name = "uid", strategy = "uuid") private String cid; private String className; public String getCid() { return cid; } public void setCid(String cid) { this.cid = cid; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } }
测试代码:
@Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st1 = new Student(); st1.setSalary(7500d); Student st2 = new Student(); st2.setSalary(17500d); ClassRoom c = new ClassRoom(); c.setClassName("大软一"); st1.setClassRoom(c); st2.setClassRoom(c); session.save(c); session.save(st1); session.save(st2); tx.commit(); session.close(); }
结果:
mysql> desc student; +---------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | | classId | varchar(40) | YES | MUL | NULL | | +---------+-------------+------+-----+---------+----------------+ 5 rows in set (0.01 sec) mysql> desc classroom; +-----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+-------+ | cid | varchar(40) | NO | PRI | NULL | | | className | varchar(255) | YES | | NULL | | +-----------+--------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> select * from classroom; +----------------------------------+-----------+ | cid | className | +----------------------------------+-----------+ | 4028818165b95ddb0165b95df06d0000 | 大软一 | +----------------------------------+-----------+ 1 row in set (0.01 sec) mysql> select * from student; +----+------+--------+------+----------------------------------+ | id | age | salary | sex | classId | +----+------+--------+------+----------------------------------+ | 1 | NULL | 7500 | NULL | 4028818165b95ddb0165b95df06d0000 | | 2 | NULL | 17500 | NULL | 4028818165b95ddb0165b95df06d0000 | +----+------+--------+------+----------------------------------+ 2 rows in set (0.00 sec)
4.一对多的单向外键关联(注意:一对多的时候多方的抓取策略设置为EAGER,一方设置为lazy)
一方持有多方的集合
例如:一个班级对应多个学生
多方:
一方:
测试代码:
@Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st1 = new Student(); st1.setSalary(7500d); Student st2 = new Student(); st2.setSalary(17500d); ClassRoom c = new ClassRoom(); c.setClassName("大软一"); c.getStudents().add(st1); c.getStudents().add(st2); session.save(st1); session.save(st2); session.save(c); tx.commit(); session.close(); }
结果:
mysql> desc classroom; +-----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+-------+ | cid | varchar(40) | NO | PRI | NULL | | | className | varchar(255) | YES | | NULL | | +-----------+--------------+------+-----+---------+-------+ 2 rows in set (0.00 sec) mysql> desc student; +-------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | | classroomId | varchar(40) | YES | MUL | NULL | | +-------------+-------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) mysql> select * from student; +----+------+--------+------+----------------------------------+ | id | age | salary | sex | classroomId | +----+------+--------+------+----------------------------------+ | 1 | NULL | 7500 | NULL | 4028818165b96ce90165b96d0c970000 | | 2 | NULL | 17500 | NULL | 4028818165b96ce90165b96d0c970000 | +----+------+--------+------+----------------------------------+ 2 rows in set (0.00 sec) mysql> select * from classroom; +----------------------------------+-----------+ | cid | className | +----------------------------------+-----------+ | 4028818165b9697c0165b96998ab0000 | 大软一 | | 4028818165b96ce90165b96d0c970000 | 大软一 | +----------------------------------+-----------+ 2 rows in set (0.00 sec)
5.多对一的双向外键关联关系(数据库仍然是一的一方存多的一方的外键)
多的一方持有一的一方的引用:
@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
@JoinColumn(name = "classroomId")
一的一方持有多的一方的集合:
// 一方持有多方的集合 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "classroomId") // 定义在student添加的外键的列名
例如:
测试代码:
@Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st1 = new Student(); st1.setSalary(7500d); Student st2 = new Student(); st2.setSalary(17500d); ClassRoom c = new ClassRoom(); c.setClassName("大软一"); c.getStudents().add(st1); c.getStudents().add(st2); st1.setClassRoom(c); st2.setClassRoom(c); session.save(st1); session.save(st2); session.save(c); tx.commit(); session.close(); }
结果:
mysql> desc student; +-------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | | classroomId | varchar(40) | YES | MUL | NULL | | +-------------+-------------+------+-----+---------+----------------+ 5 rows in set (0.02 sec) mysql> desc classroom; +-----------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+--------------+------+-----+---------+-------+ | cid | varchar(40) | NO | PRI | NULL | | | className | varchar(255) | YES | | NULL | | +-----------+--------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> select * from student; +----+------+--------+------+----------------------------------+ | id | age | salary | sex | classroomId | +----+------+--------+------+----------------------------------+ | 1 | NULL | 17500 | NULL | 4028818165b971c90165b971e4920000 | | 2 | NULL | 7500 | NULL | 4028818165b971c90165b971e4920000 | +----+------+--------+------+----------------------------------+ 2 rows in set (0.00 sec) mysql> select * from classroom; +----------------------------------+-----------+ | cid | className | +----------------------------------+-----------+ | 4028818165b971c90165b971e4920000 | 大软一 | +----------------------------------+-----------+ 1 row in set (0.00 sec)
6.多对多的单向外键关联关系
典型的应用:教师和学生之间是多对多的关系。
学生实体类有教师实体类的集合
@Entity public class Student implements Serializable { /** * */ private static final long serialVersionUID = -624145366487777133L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Integer age; private Character sex; private Double salary; @ManyToMany @JoinTable(name = "teacher_student", joinColumns = { @JoinColumn(name = "studentId") }, inverseJoinColumns = { @JoinColumn(name = "teacherId") }) private Set<Teacher> teachers = new HashSet(); ... }
JoinTable的name属性是中间表的名字,@JoinColumn是本实体类在中间表的外键列名字,inverseJoinColumns是设置对应的教师在中间表的列信息。
@Entity public class Teacher { @Id @Column(length = 40) @GeneratedValue(generator = "uid") @GenericGenerator(name = "uid", strategy = "uuid") private String teacherId; private String teacherName; 。。。 }
测试代码:
@Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st1 = new Student(); st1.setSalary(7500d); Student st2 = new Student(); st2.setSalary(17500d); Teacher t1 = new Teacher(); t1.setTeacherName("张三"); Teacher t2 = new Teacher(); t2.setTeacherName("李四"); st1.getTeachers().add(t1); st1.getTeachers().add(t2); st2.getTeachers().add(t1); st2.getTeachers().add(t2); session.save(t1); session.save(t2); session.save(st1); session.save(st2); tx.commit(); session.close(); }
结果:
mysql> desc student; +--------+------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | +--------+------------+------+-----+---------+----------------+ 4 rows in set (0.03 sec) mysql> desc teacher; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | teacherId | varchar(40) | NO | PRI | NULL | | | teacherName | varchar(255) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> desc teacher_student; +-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | id | bigint(20) | NO | PRI | NULL | | | teacherId | varchar(40) | NO | PRI | NULL | | +-----------+-------------+------+-----+---------+-------+ 2 rows in set (0.10 sec) mysql> select * from student; Empty set (0.01 sec) mysql> select * from student;desc ^C mysql> desc student; +--------+------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +--------+------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | age | int(11) | YES | | NULL | | | salary | double | YES | | NULL | | | sex | char(1) | YES | | NULL | | +--------+------------+------+-----+---------+----------------+ 4 rows in set (0.03 sec) mysql> desc teacher; +-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | teacherId | varchar(40) | NO | PRI | NULL | | | teacherName | varchar(255) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> desc teacher_student; +-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | studentId | bigint(20) | NO | PRI | NULL | | | teacherId | varchar(40) | NO | PRI | NULL | | +-----------+-------------+------+-----+---------+-------+ 2 rows in set (0.01 sec) mysql> select * from teacher; +----------------------------------+-------------+ | teacherId | teacherName | +----------------------------------+-------------+ | 4028818165b9a53b0165b9a55ae80000 | 张三 | | 4028818165b9a53b0165b9a55b470001 | 李四 | +----------------------------------+-------------+ 2 rows in set (0.06 sec) mysql> select * from student; +----+------+--------+------+ | id | age | salary | sex | +----+------+--------+------+ | 1 | NULL | 7500 | NULL | | 2 | NULL | 17500 | NULL | +----+------+--------+------+ 2 rows in set (0.00 sec) mysql> select * from teacher_student; +-----------+----------------------------------+ | studentId | teacherId | +-----------+----------------------------------+ | 1 | 4028818165b9a53b0165b9a55ae80000 | | 2 | 4028818165b9a53b0165b9a55ae80000 | | 1 | 4028818165b9a53b0165b9a55b470001 | | 2 | 4028818165b9a53b0165b9a55b470001 | +-----------+----------------------------------+ 4 rows in set (0.00 sec)
7.多对多的双向外键关联(一方维护关系,另一方放弃维护关系)
双方都有另一方的集合对象。其中一方的设置主要是一方维护映射关系,另一方配置维护关系由对方维护。下面的例子是Student维护关系,Teacher放弃维护关系
@Entity public class Student implements Serializable { /** * */ private static final long serialVersionUID = -624145366487777133L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Integer age; private Character sex; private Double salary; @ManyToMany @JoinTable(name = "teacher_student", joinColumns = { @JoinColumn(name = "studentId") }, inverseJoinColumns = { @JoinColumn(name = "teacherId") }) private Set<Teacher> teachers = new HashSet(); ... }
@Entity public class Teacher { @Id @Column(length = 40) @GeneratedValue(generator = "uid") @GenericGenerator(name = "uid", strategy = "uuid") private String teacherId; private String teacherName; @ManyToMany(mappedBy = "teachers") // 表示关系是多对多,且维护关系交给student的teachers属性 private Set<Student> students = new HashSet(); .. }
测试代码:
@Test public void test2() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); String hql = "from Teacher"; Query query = session.createQuery(hql); List<Teacher> list = query.list(); for (Teacher t : list) { System.out.println(t.getTeacherName()); System.out.println(t.getStudents().size()); } String hql1 = "from Student"; Query query1 = session.createQuery(hql1); List<Student> list1 = query1.list(); for (Student t : list1) { System.out.println(t.getSalary()); System.out.println(t.getTeachers().size()); } tx.commit(); session.close(); }
结果:
张三
2
李四
2
7500.0
2
17500.0
2
补充:
1.对于hibernate5.0之后的cascade属性默认为空,所以如果我们想级联操作需要手动配置:例如下面学生设置级联属性
测试代码:
@Test public void test1() { Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Student st1 = new Student(); st1.setSalary(7500d); Student st2 = new Student(); st2.setSalary(17500d); Teacher t1 = new Teacher(); t1.setTeacherName("张三"); Teacher t2 = new Teacher(); t2.setTeacherName("李四"); st1.getTeachers().add(t1); st1.getTeachers().add(t2); st2.getTeachers().add(t1); st2.getTeachers().add(t2); session.save(st1); session.save(st2); // session.save(t1);//设置级联操作保存学生会级联保存教师 // session.save(t2);//设置级联操作保存学生会级联保存教师 tx.commit(); session.close(); }
2.Hibernate注解也可以添加索引
(1)第一种方式:
@Column(unique = true) // 默认值是false,为true会添加索引 private Double salary;
mysql> show create table student\G *************************** 1. row *************************** Table: student Create Table: CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `age` int(11) DEFAULT NULL, `salary` double DEFAULT NULL, `sex` char(1) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_jr6uyypqhsuj4nf8lw2w5ninn` (`salary`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 1 row in set (0.01 sec)
(2)第二种方式:(使用hibernate包的Index注解)
@Index(name = "sexIndex")//该注解来自Hibernate包 private Character sex;
mysql> show create table student\G *************************** 1. row *************************** Table: student Create Table: CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `age` int(11) DEFAULT NULL, `salary` double DEFAULT NULL, `sex` char(1) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_jr6uyypqhsuj4nf8lw2w5ninn` (`salary`), KEY `sexIndex` (`sex`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
(3)第三种方式:(建议这种,方便而且可以建立组合索引)
@Entity @Table(indexes = { @Index(name = "sexSalaryIndex", columnList = "sex,salary"), @Index(name = "sexIndex", columnList = "sex", unique = true) }) public class Student implements Serializable {
mysql> show create table student\G *************************** 1. row *************************** Table: student Create Table: CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `age` int(11) DEFAULT NULL, `salary` double DEFAULT NULL, `sex` char(1) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `UK_jr6uyypqhsuj4nf8lw2w5ninn` (`salary`), KEY `sexIndex` (`sex`), KEY `sexSalaryIndex` (`sex`,`salary`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 1 row in set (0.00 sec)