基于连接表,顾名思义就是需要建立中间表来保存两个表的关联关系。与基于外键的一对多关联关系不同的是多了一个保存外键关系的关联表,通过关联表维护两个实体之间的关系。
同时,既然是一对多,一端的Set集合肯定也是少不了的,那么在一端的hbm中,肯定也会有<set>标签出现。
一个Person有多个地址,建立3表,连接表jointable保存2个实体表的主键,jointable维护实体表的关联关系。
Person.java实体类中使用集合Set,保存多端的关系,并在Person.hbm.xml文件中配置关联关系。
创建数据库脚本如下:(参看附件)
-- MySQL dump 10.13 Distrib 5.1.55, for Win32 (ia32) -- -- Host: localhost Database: hibernate_demo -- ------------------------------------------------------ -- Server version 5.1.55-community /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `address` -- DROP TABLE IF EXISTS `address`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `address` ( `addressId` int(11) NOT NULL AUTO_INCREMENT, `item` varchar(30) NOT NULL, PRIMARY KEY (`addressId`) ) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `address` -- LOCK TABLES `address` WRITE; /*!40000 ALTER TABLE `address` DISABLE KEYS */; INSERT INTO `address` VALUES (1,'address_no2'),(2,'address_no1'); /*!40000 ALTER TABLE `address` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `person` -- DROP TABLE IF EXISTS `person`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `person` ( `personid` int(11) NOT NULL AUTO_INCREMENT, `age` int(11) NOT NULL, `sex` varchar(10) NOT NULL, PRIMARY KEY (`personid`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `person` -- LOCK TABLES `person` WRITE; /*!40000 ALTER TABLE `person` DISABLE KEYS */; INSERT INTO `person` VALUES (1,10,'M'); /*!40000 ALTER TABLE `person` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `personadd` -- DROP TABLE IF EXISTS `personadd`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `personadd` ( `pid` int(11) NOT NULL, `aid` int(11) NOT NULL, KEY `pid` (`pid`), KEY `aid` (`aid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `personadd` -- LOCK TABLES `personadd` WRITE; /*!40000 ALTER TABLE `personadd` DISABLE KEYS */; INSERT INTO `personadd` VALUES (1,1),(1,2); /*!40000 ALTER TABLE `personadd` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2012-08-09 23:04:56
//Address.java
public class Address implements java.io.Serializable { // Fields private Integer addressId; private String item;
//Person.java
public class Person implements java.io.Serializable { // Fields private Integer personid; private Integer age; private String sex; private Set<Address> address = new HashSet<Address>();
//Person.hbm.xml
<!-- 基于连接表的一对多单项关联 --> <!-- set name表示Person表中的Address集合,table是关联表名,cascade级联操作 --> <!-- key column是一端主键在关联表中的外键名 --> <!-- many-to-many column是多端主键在关联表中的外键名 --> <set name="address" table="personadd" cascade="save-update"> <key column="pid"/> <many-to-many column="aid" unique="true" class="com.v512.examples.Address"/> </set>
保存数据
//HibernateTest.java
public static void addInfo() { Address ad1 = new Address(); ad1.setItem("address_no1"); Address ad2 = new Address(); ad2.setItem("address_no2"); Person person = new Person(); person.setAge(10); person.setSex("M"); Set<Address> address = new HashSet<Address>(); address.add(ad1); address.add(ad2); person.setAddress(address); Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); //启用级联操作 session.save(person); // session.save(ad1); // session.save(ad2); tx.commit(); session.close(); HibernateUtil.shutdown(); }
操作信息如下:
22:59:30,843 DEBUG ConnectionManager:444 - opening JDBC connection 22:59:30,843 DEBUG JDBCTransaction:87 - current autocommit status: false 22:59:30,843 DEBUG IncrementGenerator:104 - fetching initial value: select max(personid) from person 22:59:30,843 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 22:59:30,859 DEBUG SQL:111 - select max(personid) from person Hibernate: select max(personid) from person 22:59:30,859 DEBUG IncrementGenerator:119 - first free id: 1 22:59:30,859 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 22:59:30,875 DEBUG AbstractSaveEventListener:135 - generated identifier: 1, using strategy: org.hibernate.id.IncrementGenerator 22:59:30,890 DEBUG IncrementGenerator:104 - fetching initial value: select max(addressId) from address 22:59:30,890 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 22:59:30,890 DEBUG SQL:111 - select max(addressId) from address Hibernate: select max(addressId) from address 22:59:30,890 DEBUG IncrementGenerator:119 - first free id: 1 22:59:30,890 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 22:59:30,937 DEBUG AbstractSaveEventListener:135 - generated identifier: 1, using strategy: org.hibernate.id.IncrementGenerator 22:59:30,937 DEBUG AbstractSaveEventListener:135 - generated identifier: 2, using strategy: org.hibernate.id.IncrementGenerator 22:59:30,937 DEBUG JDBCTransaction:134 - commit 22:59:30,937 DEBUG AbstractFlushingEventListener:134 - processing flush-time cascades 22:59:30,937 DEBUG AbstractFlushingEventListener:177 - dirty checking collections 22:59:30,937 DEBUG Collections:199 - Collection found: [com.v512.examples.Person.address#1], was: [<unreferenced>] (initialized) 22:59:30,984 DEBUG AbstractFlushingEventListener:108 - Flushed: 3 insertions, 0 updates, 0 deletions to 3 objects 22:59:30,984 DEBUG AbstractFlushingEventListener:114 - Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections 22:59:30,984 DEBUG Printer:106 - listing entities: 22:59:30,984 DEBUG Printer:113 - com.v512.examples.Person{sex=M, address=[com.v512.examples.Address#1, com.v512.examples.Address#2], age=10, personid=1} 22:59:30,984 DEBUG Printer:113 - com.v512.examples.Address{item=address_no1, addressId=2} 22:59:30,984 DEBUG Printer:113 - com.v512.examples.Address{item=address_no2, addressId=1} 22:59:30,984 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 22:59:30,984 DEBUG SQL:111 - insert into hibernate_demo.person (age, sex, personid) values (?, ?, ?) Hibernate: insert into hibernate_demo.person (age, sex, personid) values (?, ?, ?) 22:59:30,984 DEBUG AbstractBatcher:66 - Executing batch size: 1 22:59:30,984 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 22:59:30,984 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 22:59:30,984 DEBUG SQL:111 - insert into hibernate_demo.address (item, addressId) values (?, ?) Hibernate: insert into hibernate_demo.address (item, addressId) values (?, ?) 22:59:31,000 DEBUG AbstractBatcher:248 - reusing prepared statement 22:59:31,000 DEBUG SQL:111 - insert into hibernate_demo.address (item, addressId) values (?, ?) Hibernate: insert into hibernate_demo.address (item, addressId) values (?, ?) 22:59:31,000 DEBUG AbstractBatcher:66 - Executing batch size: 2 22:59:31,000 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 22:59:31,000 DEBUG AbstractCollectionPersister:1112 - Inserting collection: [com.v512.examples.Person.address#1] 22:59:31,000 DEBUG AbstractBatcher:410 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 22:59:31,000 DEBUG SQL:111 - insert into personadd (pid, aid) values (?, ?) Hibernate: insert into personadd (pid, aid) values (?, ?) 22:59:31,000 DEBUG AbstractBatcher:248 - reusing prepared statement 22:59:31,000 DEBUG SQL:111 - insert into personadd (pid, aid) values (?, ?) Hibernate: insert into personadd (pid, aid) values (?, ?) 22:59:31,000 DEBUG AbstractCollectionPersister:1194 - done inserting collection: 2 rows inserted 22:59:31,000 DEBUG AbstractBatcher:66 - Executing batch size: 2 22:59:31,000 DEBUG AbstractBatcher:418 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 22:59:31,000 DEBUG JDBCTransaction:147 - committed JDBC Connection 22:59:31,000 DEBUG ConnectionManager:427 - aggressively releasing JDBC connection 22:59:31,000 DEBUG ConnectionManager:464 - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 22:59:31,000 INFO SessionFactoryImpl:853 - closing 22:59:31,000 INFO DriverManagerConnectionProvider:170 - cleaning up connection pool: jdbc:mysql://localhost:3306/hibernate_demo
其他的代码都是通过myeclipse生成的,不是很麻烦。
刚开始大家可能会有疑惑,是呀,有3个表,为什么不建3个类,3个hbm文件呢?
事实是这样的,数据库之间的表关联关系通过外键实现,现在有了外键关系表。而实体呢?实体之间是通过集合类进行关联的,我们在Person.hbm.xml文件中配置了一对多的关联关系,自然也不同在建立什么关联关系表的映射文件了,对吧。况且,2个文件能做的事情,何必用3个文件呢。