Hibernate继承关系

1、整个继承树映射到一张表

在一个论坛中有用户(User)这个实体类,其中User这个实体中有分为普通用户,管理员与游客这3种身份,而这3种现在身份的用户就只有一个字段(即类型)不相同,可以考虑用Hibernate中的Table per class hierarchy策略

 

对象模型(Java类结构)

表结构

 User代码:

package org.hibernate.domain;

import java.util.Date;

/**
 * 实体类
 */
public class User {
	private int id;
	private String name;
	private Date birthday;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", birthday=" + birthday
				+ "]";
	}

}

 

Admin代码:

package org.hibernate.domain;

public class Admin extends User {
	private String admin;

	public String getAdmin() {
		return admin;
	}

	public void setAdmin(String admin) {
		this.admin = admin;
	}

}

 

package org.hibernate.domain;

public class Guest extends User {
	private String guest;

	public String getGuest() {
		return guest;
	}

	public void setGuest(String guest) {
		this.guest = guest;
	}

}

 

配置文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.domain">
	<class name="User" table="user" discriminator-value="0">
		<id name="id">
			<generator class="native" />
		</id>
		
		<!-- 默认为string类型,这里指定为int类型  -->
		<discriminator column="type" type="int" />

		<property name="name" />
		<property name="birthday" />
		
		<subclass name="Admin" discriminator-value="1">
			<property name="admin" column="admin" />
		</subclass>
		
		<subclass name="Guest" discriminator-value="2">
			<property name="guest" column="guest" />
		</subclass>
	</class>
</hibernate-mapping>

 

测试类:

package org.hibernate.test;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.domain.Admin;
import org.hibernate.domain.Guest;
import org.hibernate.domain.User;
import org.hibernate.util.HibernateUtil;

public class TestUser extends TestCase {
	public void testAddUser() {
		Session session = null;
		Transaction tx = null;

		try {
			User user = new User();
			user.setName("user1");
			
			Admin admin = new Admin();
			admin.setName("user2");
			admin.setAdmin("admin user");

			Guest guest = new Guest();
			guest.setName("user3");
			guest.setGuest("guest user");
			
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.save(user);
			session.save(admin);
			session.save(guest);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
	
	public void testQuery() {
		Session session = null;
		Transaction tx = null;

		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.get(User.class, 1);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
}

 

testAddUser方法测试结果: 

Hibernate: insert into user (name, birthday, type) values (?, ?, 0)
Hibernate: insert into user (name, birthday, admin, type) values (?, ?, ?, 1)
Hibernate: insert into user (name, birthday, guest, type) values (?, ?, ?, 2)

 可以看到3个对象都保存在了同一张表(User)中,其中靠type字段来区分不同的用户

 

User表记录如下

 

现在我们测试testQuery方法,结果如下:

Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.birthday as birthday0_0_, user0_.admin as admin0_0_, user0_.guest as guest0_0_, user0_.type as type0_0_ from user user0_ where user0_.id=?

 将测试代码中的

session.get(User.class, 1);

 更改为

  session.get(User.class, 2);

   则结果如下: 

Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_, user0_.birthday as birthday0_0_, user0_.admin as admin0_0_, user0_.guest as guest0_0_, user0_.type as type0_0_ from user user0_ where user0_.id=?

 因为hibernate支持多态查询,故查询语句不变

 

将测试代码中的

session.get(User.class, 2);

 更改为

session.get(Guest.class, 2);

则结果如下:

Hibernate: select guest0_.id as id0_0_, guest0_.name as name0_0_, guest0_.birthday as birthday0_0_, guest0_.guest as guest0_0_ from user guest0_ where guest0_.id=? and guest0_.type=2

   从上述结果可以看到如果是明确知道查询是哪一类用户,则查询语句后面会指定type类型

 

采用这种策略只需要一张表即可。它有一个很大的限制:要求那些由子类定义的字段, 如admin,不能有非空(NOT NULL)约束。

 

    2、每个类映射到一张表

可以看到Hibernate整个类映射到一张表策略中,如果子类中有很多字段的情况下,则表就会产生很多的空字段,由此当子类中有很多字段的情况下,可以采用“每个子类一张表”的映射策略

 

表结构如下:

修改User.hbm.xml映射文件如下:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.domain">
	<class name="User" table="user">
		<id name="id" column="userId">
			<generator class="native" />
		</id>
		
		<property name="name" />
		<property name="birthday" />
		
		<joined-subclass name="Admin" table="admin">
			<key column="userId" />
			<property name="admin" column="admin" />
		</joined-subclass>
		
		<joined-subclass name="Guest" table="guest">
			<key column="userId" />
			<property name="guest" column="guest" />
		</joined-subclass>
	</class>
</hibernate-mapping>

 

测试类:

package org.hibernate.test;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.domain.Admin;
import org.hibernate.domain.Guest;
import org.hibernate.domain.User;
import org.hibernate.util.HibernateUtil;

public class TestUser extends TestCase {
	public void testAddUser() {
		Session session = null;
		Transaction tx = null;

		try {
			User user = new User();
			user.setName("user1");
			
			Admin admin = new Admin();
			admin.setName("user2");
			admin.setAdmin("admin user");

			Guest guest = new Guest();
			guest.setName("user3");
			guest.setGuest("guest user");
			
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.save(user);
			session.save(admin);
			session.save(guest);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
	
	public void testQuery() {
		Session session = null;
		Transaction tx = null;

		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.get(User.class, 1);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
}

 

testAddUser方法测试结果:

Hibernate: insert into user (name, birthday) values (?, ?)
Hibernate: insert into user (name, birthday) values (?, ?)
Hibernate: insert into admin (admin, userId) values (?, ?)
Hibernate: insert into user (name, birthday) values (?, ?)
Hibernate: insert into guest (guest, userId) values (?, ?)

 当插入的用户不是普通用户的时候,需要往2张表中插入数据

数据库表记录如下:

 

 
  测试testQuery方法,结果如下:
Hibernate: select user0_.userId as userId0_0_, user0_.name as name0_0_, user0_.birthday as birthday0_0_, user0_1_.admin as admin1_0_, user0_2_.guest as guest2_0_, case when user0_1_.userId is not null then 1 when user0_2_.userId is not null then 2 when user0_.userId is not null then 0 end as clazz_0_ from user user0_ left outer join admin user0_1_ on user0_.userId=user0_1_.userId left outer join guest user0_2_ on user0_.userId=user0_2_.userId where user0_.userId=?
 将测试代码中的
session.get(User.class, 1);
 更改为 
session.get(Admin.class, 1);
 则结果如下:
Hibernate: select admin0_.userId as userId0_0_, admin0_1_.name as name0_0_, admin0_1_.birthday as birthday0_0_, admin0_.admin as admin1_0_ from admin admin0_ inner join user admin0_1_ on admin0_.userId=admin0_1_.userId where admin0_.userId=?
 相比前面的查询语句,这句就简单的多了,所以我们在用“每个子类一张表”的映射策略的时候,最好明确要查询的类,这样可以提高查询的效率 
 
3、混合使用“整个继承树映射到一张表 ”和“每个子类一张表”的映射策略
假设,guest用户的属性很少,而admin的属性很多,则可以使用混合使用“整个继承树映射到一张  表 ”和“每个子类一张表””的映射策略
测试类:
package org.hibernate.test;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.domain.Admin;
import org.hibernate.domain.Guest;
import org.hibernate.domain.User;
import org.hibernate.util.HibernateUtil;

public class TestUser extends TestCase {
	public void testAddUser() {
		Session session = null;
		Transaction tx = null;

		try {
			User user = new User();
			user.setName("user1");
			
			Admin admin = new Admin();
			admin.setName("user2");
			admin.setAdmin("admin user");

			Guest guest = new Guest();
			guest.setName("user3");
			guest.setGuest("guest user");
			
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.save(user);
			session.save(admin);
			session.save(guest);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
	
	public void testQuery() {
		Session session = null;
		Transaction tx = null;

		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.get(User.class, 1);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
}
 
如果在hibernate.cfg.xml配置中配置了如下项:
<property name="hbm2ddl.auto">create</property>
 此时测试testAddUser方法时,测试单元会报如下错误:
程序运行时,会删除数据库中的user表,admin表,而user表中有admin表的外键关联,所以不能删除user数据表了,所以抛出了上面的异常。所以需要先手动删除数据表,再运行程序。
此时测试testAdd方法时,结果如下: 
Hibernate: insert into user (name, birthday, type) values (?, ?, 0)
Hibernate: insert into user (name, birthday, type) values (?, ?, 2)
Hibernate: insert into admin (admin, userId) values (?, ?)
Hibernate: insert into user (name, birthday, guest, type) values (?, ?, ?, 1)
与采用“每个子类一张表”的映射策略相比,插入数据的时候,guest只插入一张表,admin插入了2张表,效率提高了些,如果要查询数据,则当查询admin的时候才会使用连接查询  
 
4、"每个具体类一张表"的映射策略
前面第一种映射策略中表结构存在null字段,第二种与第三种中查询的时候需要用到表连接,采用"每个具体类一张表"的映射策略则不会出现以上情况
表结构:
测试类:
package org.hibernate.test;

import junit.framework.TestCase;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.domain.Admin;
import org.hibernate.domain.Guest;
import org.hibernate.domain.User;
import org.hibernate.util.HibernateUtil;

public class TestUser extends TestCase {
	public void testAddUser() {
		Session session = null;
		Transaction tx = null;

		try {
			User user = new User();
			user.setName("user1");
			
			Admin admin = new Admin();
			admin.setName("user2");
			admin.setAdmin("admin user");

			Guest guest = new Guest();
			guest.setName("user3");
			guest.setGuest("guest user");
			
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.save(user);
			session.save(admin);
			session.save(guest);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
	
	public void testQuery() {
		Session session = null;
		Transaction tx = null;

		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			session.get(User.class, 1);
			tx.commit();
		} finally {
			if (session != null)
				session.close();
		}
	}
}
 

配置文件:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.domain">
	<class name="User" table="user">
		<id name="id" column="userId">
			<generator class="hilo" />
		</id>
		
		<property name="name" />
		<property name="birthday" />
		
		<union-subclass name="Admin" table="admin">
			<property name="admin" />
		</union-subclass>
		
		<union-subclass name="Guest" table="guest">
			<property name="guest" />
		</union-subclass>
	</class>
</hibernate-mapping>

 

 testAddUser方法测试结果:

Hibernate: insert into user (name, birthday, userId) values (?, ?, ?)
Hibernate: insert into admin (name, birthday, admin, userId) values (?, ?, ?, ?)
Hibernate: insert into guest (name, birthday, guest, userId) values (?, ?, ?, ?)
可以看到3个对象分别插入到3张表中
testQuery方法测试结果:
Hibernate: select user0_.userId as userId0_0_, user0_.name as name0_0_, user0_.birthday as birthday0_0_, user0_.admin as admin1_0_, user0_.guest as guest2_0_, user0_.clazz_ as clazz_0_ from ( select birthday, null as admin, name, userId, null as guest, 0 as clazz_ from user union select birthday, admin, name, userId, null as guest, 1 as clazz_ from admin union select birthday, null as admin, name, userId, guest, 2 as clazz_ from guest ) user0_ where user0_.userId=?
可以看到Hibernate会把所有的表都连接起来做查询,然后再从中查询出符合条件的记录 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成Hibernate可以实现方便的数据库操作。下面是一些步骤来实现集成: 1. 添加依赖:在项目的pom.xml文件中添加Spring Boot和Hibernate的依赖。例如: ```xml <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> </dependencies> ``` 2. 配置数据源:在应用的配置文件(如application.properties或application.yml)中配置数据库连接信息。例如: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=username spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect ``` 3. 定义实体类:创建与数据库表对应的实体类,并使用注解来定义实体类与表的映射关系。例如: ```java @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // getters and setters } ``` 4. 创建Repository:创建一个继承自`JpaRepository`的接口,用于定义对实体类进行CRUD操作的方法。例如: ```java @Repository public interface UserRepository extends JpaRepository<User, Long> { // 自定义查询方法 User findByName(String name); } ``` 5. 使用Hibernate进行数据库操作:在需要进行数据库操作的地方,通过注入Repository来使用Hibernate进行操作。例如: ```java @Service public class UserService { @Autowired private UserRepository userRepository; public User getUserByName(String name) { return userRepository.findByName(name); } // 其他业务逻辑方法 } ``` 这样,你就可以在Spring Boot中使用Hibernate进行数据库操作了。注意,以上只是一个简单的示例,实际项目中可能需要更多的配置和细节处理。你可以根据自己的需求来进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值