第二十一部分_Hibernate级联关系详解
接下来我们开始介绍Hibernate的数据类型,因为我们现在暂时只关注Hibernate这块,因此我们这次只建立一个Java Project,命名为hibernate2。
加入hibernate JAR包:
选择hibernate2项目,点击MyEclipse->Add Hibernate Capabilities, Hibernate Specification与风中页老师的相同,为Hibernate3.2,点击next,继续next,去掉Specify database connection details前面的√接着next,去掉Create SessionFactory class?前面的√点击Finish。
把上一个hibernate项目的hibernate.cfg.xml文件拷贝过来,覆盖掉当前src下面的hibernate.cfg.xml文件,修改mapping信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version= '1.0' encoding= 'UTF-8' ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" > <!-- Generated by MyEclipse Hibernate Tools. --> <hibernate-configuration> <session-factory> <property name= "show_sql" > true </property> <!-- 属性之间没有上下关系,放在哪里都行 --> <property name= "connection.url" >jdbc:mysql: //localhost:3306/myhibernate2</property> <property name= "connection.username" >root</property> <property name= "connection.password" >root</property> <property name= "connection.driver_class" >com.mysql.jdbc.Driver</property> <property name= "dialect" >org.hibernate.dialect.MySQLDialect</property> <mapping resource= "Customers.hbm.xml" /> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 --> </session-factory> </hibernate-configuration> |
添加MySql驱动,从hibernate项目拷贝mysql-connector-java-5.1.34-bin.jar到hibernate根目录下。
创建表:(bigint即long类型;bit即boolean类型;timestamp也是一个日期类型的,比date精度更高,可以精确到毫秒;blob即二进制大型物件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | mysql> create table CUSTOMERS( -> ID bigint not null primary key, -> NAME varchar( 15 ) not null , -> EMAIL varchar( 128 ) not null , -> PASSWORD varchar( 8 ) not null , -> PHONE int , -> ADDRESS varchar( 255 ), -> SEX char ( 1 ), -> IS_MARRIED bit, -> DESCRIPTION text, -> IMAGE blob, -> BIRTHDAY date, -> REGISTERED_TIME timestamp -> ); |
新建com.test.bean包,在该包下面新建一个类Customer.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | package com.test.bean; import java.sql.Date; import java.sql.Timestamp; public class Customer { private Long id; private String name; private String email; private String password; private int phone; // or Integer private String address; private char sex; private boolean married; private String description; private byte [] image; private Date birthday; private Timestamp registeredTime; public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public String getEmail() { return email; } public void setEmail(String email) { this .email = email; } public String getPassword() { return password; } public void setPassword(String password) { this .password = password; } public int getPhone() { return phone; } public void setPhone( int phone) { this .phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this .address = address; } public char getSex() { return sex; } public void setSex( char sex) { this .sex = sex; } public boolean isMarried() { return married; } public void setMarried( boolean married) { this .married = married; } public String getDescription() { return description; } public void setDescription(String description) { this .description = description; } public byte [] getImage() { return image; } public void setImage( byte [] image) { this .image = image; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this .birthday = birthday; } public Timestamp getRegisteredTime() { return registeredTime; } public void setRegisteredTime(Timestamp registeredTime) { this .registeredTime = registeredTime; } } |
在src下面新建一个Customers.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "com.test.bean.Customer" table= "customers" > <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 --> <id name= "id" column= "id" type= "long" > <!-- 类中id属性和映射到表中的id字段,类型为 int /integer皆可 --> <generator class = "increment" > <!-- 主键id的生成方式为自增 --> </generator> </id> <property name= "name" column= "name" type= "string" not- null = "true" ></property> <!-- 如果不写字段名,则默认与类中的属性名相同 ;hibernate层和数据库层都可以对非空进行检查--> <property name= "email" column= "email" type= "string" not- null = "true" ></property> <property name= "password" column= "password" type= "string" not- null = "true" ></property> <property name= "phone" column= "phone" type= "int" ></property> <property name= "address" column= "address" type= "string" ></property> <property name= "sex" column= "sex" type= "character" ></property> <property name= "married" column= "is_married" type= "boolean" ></property> <property name= "description" column= "description" type= "text" ></property> <property name= "image" column= "image" type= "binary" ></property> <property name= "birthday" column= "birthday" type= "date" ></property> <property name= "registeredTime" column= "registered_time" type= "timestamp" ></property> </ class > </hibernate-mapping> |
在com.test.bean包下面,创建测试类HibernateTest.java同时放置一个photo.gif文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | package com.test.bean; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.sql.Date; import java.util.Iterator; import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class HibernateTest { public static SessionFactory sessionFactory; static { try { Configuration config = new Configuration().configure(); sessionFactory = config.buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); } } public void findAllCustomers(PrintStream out) throws Exception { // Ask for a session using the JDBC information we've configured Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); List customers = session.createQuery( "from Customer as c order by c.name asc" ).list(); for (Iterator it = customers.iterator(); it.hasNext();) { printCustomer(out, (Customer) it.next()); } // We're done; make our changes permanent tx.commit(); } catch (Exception e) { if (tx != null ) { // Something went wrong; discard all partial changes tx.rollback(); } throw e; } finally { // No matter what, close the session session.close(); } } public static void saveCustomer(Customer customer) throws Exception { // Ask for a session using the JDBC information we've configured Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); session.save(customer); // We're done; make our changes permanent tx.commit(); } catch (Exception e) { if (tx != null ) { // Something went wrong; discard all partial changes tx.rollback(); } throw e; } finally { // No matter what, close the session session.close(); } } public void loadAndUpdateCustomer(Long customer_id, String address) throws Exception { // Ask for a session using the JDBC information we've configured Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); Customer c = (Customer) session.load(Customer. class , customer_id); c.setAddress(address); // We're done; make our changes permanent tx.commit(); } catch (Exception e) { if (tx != null ) { // Something went wrong; discard all partial changes tx.rollback(); } throw e; } finally { // No matter what, close the session session.close(); } } public void deleteAllCustomers() throws Exception { // Ask for a session using the JDBC information we've configured Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); Query query = session.createQuery( "from Customer" ); List list = query.list(); for (Iterator i = list.iterator(); i.hasNext();) { session.delete((Customer) i.next()); } tx.commit(); } catch (Exception e) { if (tx != null ) { // Something went wrong; discard all partial changes tx.rollback(); } throw e; } finally { // No matter what, close the session session.close(); } } private void printCustomer(PrintStream out, Customer customer) throws Exception { byte [] buffer = customer.getImage(); OutputStream fout = new FileOutputStream( "photo_copy.gif" ); fout.write(buffer); fout.close(); out.println( "------以下是" + customer.getName() + "的个人信息------" ); out.println( "ID: " + customer.getId()); out.println( "口令: " + customer.getPassword()); out.println( "E-Mail: " + customer.getEmail()); out.println( "电话: " + customer.getPhone()); out.println( "地址: " + customer.getAddress()); String sex = customer.getSex() == 'M' ? "男" : "女" ; out.println( "性别: " + sex); String marriedStatus = customer.isMarried() ? "已婚" : "未婚" ; out.println( "婚姻状况: " + marriedStatus); out.println( "生日: " + customer.getBirthday()); out.println( "注册时间: " + customer.getRegisteredTime()); out.println( "自我介绍: " + customer.getDescription()); } public void test(PrintStream out) throws Exception { Customer customer = new Customer(); customer.setName( "zhangsan" ); customer.setEmail( "zhangsan@yahoo.com" ); customer.setPassword( "1234" ); customer.setPhone( 1381234 ); customer.setAddress( "Shanghai" ); customer.setSex( 'M' ); customer.setDescription( "I am very honest." ); InputStream in = this .getClass().getResourceAsStream( "photo.gif" ); byte [] buffer = new byte [in.available()]; in.read(buffer); customer.setImage(buffer); customer.setBirthday(Date.valueOf( "1980-05-06" )); saveCustomer(customer); findAllCustomers(out); loadAndUpdateCustomer(customer.getId(), "Tianjin" ); findAllCustomers(out); deleteAllCustomers(); } public static void main(String args[]) throws Exception { new HibernateTest().test(System.out); sessionFactory.close(); } } |
通过给一下几行代码添加注释的方式进行测试:
1 2 3 4 5 6 7 8 9 | saveCustomer(customer); findAllCustomers(out); loadAndUpdateCustomer(customer.getId(), "Tianjin" ); findAllCustomers(out); deleteAllCustomers(); |
运行后在hibernate2根目录下面会生成一个photo_copy.gif图形文件。
上面的例子比较简单,下面我们看一个复杂的:表与表之间存在关联关系,类与类之间存在关联关系:
新建一个名为hibernate3的Java Project,导入相应的hibernate包(操作过程如之前所述)。
配置hibernate.cfg.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version= '1.0' encoding= 'UTF-8' ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" > <!-- Generated by MyEclipse Hibernate Tools. --> <hibernate-configuration> <session-factory> <property name= "show_sql" > true </property> <!-- 属性之间没有上下关系,放在哪里都行 --> <property name= "connection.url" >jdbc:mysql: //localhost:3306/myhibernate3</property> <property name= "connection.username" >root</property> <property name= "connection.password" >root</property> <property name= "connection.driver_class" >com.mysql.jdbc.Driver</property> <property name= "dialect" >org.hibernate.dialect.MySQLDialect</property> <mapping resource= "Customer.hbm.xml" /> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 --> <mapping resource= "Order.hbm.xml" /> </session-factory> </hibernate-configuration> |
接下来创建两个域模型,一个是Customer,一个是Order:
src下面新建包com.test,在该包下面新建类:Customer.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | package com.test; import java.util.Set; public class Customer { private Long id; private String name; private Set orders; // 一对多,通过该变量可以引用到对应的Order集合对象 public Customer(String name, Set orders) { this .name = name; this .orders = orders; } // hibernate一般要求我们提供一个不带参数的构造方法,为了符合hibernate要求,因此: public Customer() { } public Customer(Set orders) { this .orders = orders; } public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Set getOrders() { return orders; } public void setOrders(Set orders) { this .orders = orders; } } |
接着新建与Customer成多对一关系的Order.java类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | package com.test; public class Order { private Long id; private String orderNumber; private Customer customer; // 多对一,通过该变量可以引用到对应的Customer public Order(String orderNumber, Customer customer) { this .orderNumber = orderNumber; this .customer = customer; } public Order() { } public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this .orderNumber = orderNumber; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this .customer = customer; } } |
下面,在数据模型,写配置文件,在这之前需要建立两张表:
数据库Schema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | create table customers( ID bigint not null , NAME varchar( 15 ), primary key(ID) ); create table orders( ID bigint not null , ORDER_NUMBER varchar( 15 ), CUSTOMER_ID bigint not null , primary key(ID) ); alter table orders add index IDX_CUSTOMER_ID(CUSTOMER_ID), add constraint FK_CUSTOMER_ID foreign key (CUSTOMER_ID) references customers(ID); |
接下来在src下创建Customer.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "com.test.Customer" table= "customers" > <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 --> <id name= "id" column= "id" type= "long" > <!-- 类中id属性和映射到表中的id字段,类型为 int /integer皆可 --> <generator class = "increment" > <!-- 主键id的生成方式为自增 --> </generator> </id> <property name= "name" type= "string" > <column name= "name" length= "15" ></column> <!-- 第二种定义column的方式,可以进行精细化配置 --> </property> <set name= "orders" cascade= "save-update" inverse= "true" > <!-- 反转属性为 true ,表示关联关系由多的一方维持,这是hibernate的一个最佳实践。 --> <key column= "customer_id" ></key> <!-- key元素设定与所关联的持久化类对应的表的外键 --> <one-to-many class = "com.test.Order" /> </set> </ class > </hibernate-mapping> |
继续在src下创建Order.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "com.test.Order" table= "orders" > <id name= "id" column= "id" type= "long" > <generator class = "increment" > </generator> </id> <property name= "orderNumber" type= "string" > <column name= "order_number" length= "15" ></column> </property> <many-to-one name= "customer" column= "customer_id" class = "com.test.Customer" > </many-to-one> </ class > </hibernate-mapping> |
最后,不要忘记引入mysql的驱动:mysql-connector-java-5.1.34-bin.jar
然后,为了验证我们的配置是否正确且如愿生效,我们编写一个测试类,在com.test包下面新建一个类Test.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | package com.test; import java.util.HashSet; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class Test { public static SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure(). buildSessionFactory(); } catch (Exception ex) { System.out.println( "exeception occured" ); ex.printStackTrace(); } } public static void saveCustomerAndOrderWithCascade() throws Exception { Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); Customer customer = new Customer( "zhangsan" , new HashSet()); Order order = new Order(); order.setOrderNumber( "zhangsan_order1" ); Order order2 = new Order(); order2.setOrderNumber( "zhangsan_order2" ); Order order3 = new Order(); order3.setOrderNumber( "zhangsan_order3" ); order.setCustomer(customer); // 将一的一方关联到多的一方 order2.setCustomer(customer); order3.setCustomer(customer); customer.getOrders().add(order); // 将多的一方增加到一的一方 customer.getOrders().add(order2); customer.getOrders().add(order3); session.save(customer); tx.commit(); } catch (Exception ex) { if ( null != tx) tx.rollback(); ex.printStackTrace(); } finally { if ( null != session) session.close(); } } public static void main(String[] args) throws Exception { saveCustomerAndOrderWithCascade(); } } |
执行结果:Console输出:
1 2 3 4 5 6 | Hibernate: select max(id) from customers Hibernate: select max(id) from orders Hibernate: insert into customers (name, id) values (?, ?) Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?) Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?) Hibernate: insert into orders (order_number, customer_id, id) values (?, ?, ?) |
进入数据库中查看,我们发现orders表中的外键已经关联到customers表中的主键上了。
补充图例:
下面,继续进行探索,对于树模型的关系——找到一个节点,能找到它的多个子节点(或者没有)和唯一(或者没有)的父节点,如:
首先建立域模型:在hibernate3项目下的com.test包下建立类:Category.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | package com.test; import java.util.Set; public class Category { private Long id; private String name; private Category parentCategory; private Set childCategories; public Category(String name, Category parentCategory, Set childCategories) { this .name = name; this .parentCategory = parentCategory; this .childCategories = childCategories; } public Category() { } public Category(Set childCategories) { this .childCategories = childCategories; } public Long getId() { return id; } public void setId(Long id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Category getParentCategory() { return parentCategory; } public void setParentCategory(Category parentCategory) { this .parentCategory = parentCategory; } public Set getChildCategories() { return childCategories; } public void setChildCategories(Set childCategories) { this .childCategories = childCategories; } } |
建立表,数据库Schema:
1 2 3 4 5 6 7 8 9 | create table categories( ID bigint not null , NAME varchar( 15 ), CATEGORY_ID bigint, primary key(ID) ); alter table categories add index IDX_CATEGORY_ID(CATEGORY_ID), add constraint FK_CATEGORY_ID foreign key(CATEGORY_ID) references categories(ID); |
建立Category.hbm.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?xml version= "1.0" encoding= "UTF-8" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "com.test.Category" table= "categories" > <!-- 将类与表相关联,使得类中的属性和表中的字段关联起来 --> <id name= "id" column= "id" type= "long" > <!-- 类中id属性和映射到表中的id字段,类型为 int /integer皆可 --> <generator class = "increment" > <!-- 主键id的生成方式为自增 --> </generator> </id> <property name= "name" type= "string" > <column name= "name" length= "15" ></column> <!-- 第二种定义column的方式,可以进行精细化配置 --> </property> <!-- 反转属性为 true ,表示关联关系由多的一方维持,这是hibernate的一个最佳实践。 --> <set name= "childCategories" cascade= "all" inverse= "true" > <!-- cascade= "all" 表示保存、更新或删除当前对象时级联其他关联的对象 --> <key column= "category_id" ></key> <!-- key元素设定与所关联的持久化类对应的表的外键 --> <one-to-many class = "com.test.Category" /> </set> <many-to-one name= "parentCategory" column= "category_id" class = "com.test.Category" > </many-to-one> </ class > </hibernate-mapping> |
接下来,在Test.java中增加方法——saveCategoryWithCascade和deleteCategoryWithCascade:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | package com.test; import java.util.HashSet; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class Test { public static SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure(). buildSessionFactory(); } catch (Exception ex) { System.out.println( "exeception occured" ); ex.printStackTrace(); } } public static void saveCustomerAndOrderWithCascade() throws Exception { Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); Customer customer = new Customer( "zhangsan" , new HashSet()); Order order = new Order(); order.setOrderNumber( "zhangsan_order1" ); Order order2 = new Order(); order2.setOrderNumber( "zhangsan_order2" ); Order order3 = new Order(); order3.setOrderNumber( "zhangsan_order3" ); order.setCustomer(customer); // 将一的一方关联到多的一方 order2.setCustomer(customer); order3.setCustomer(customer); customer.getOrders().add(order); // 将多的一方增加到一的一方 customer.getOrders().add(order2); customer.getOrders().add(order3); session.save(customer); tx.commit(); } catch (Exception ex) { if ( null != tx) tx.rollback(); ex.printStackTrace(); } finally { if ( null != session) session.close(); } } @SuppressWarnings ( "unchecked" ) public static void saveCategoryWithCascade() throws Exception { Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); // 根据图例,创建食品类别、蔬菜类别、水果类别、西红柿类别、苹果类别、桔子类别。 Category foodCategory = new Category( "food" , null , new HashSet()); Category fruitCategory = new Category( "fruit" , null , new HashSet()); Category vegetableCategory = new Category( "vegetable" , null , new HashSet()); Category appleCategory = new Category( "apple" , null , new HashSet()); Category orangeCategory = new Category( "orange" , null , new HashSet()); Category tomatoCategory = new Category( "tomato" , null , new HashSet()); foodCategory.getChildCategories().add(fruitCategory); fruitCategory.setParentCategory(foodCategory); foodCategory.getChildCategories().add(vegetableCategory); vegetableCategory.setParentCategory(foodCategory); fruitCategory.getChildCategories().add(appleCategory); appleCategory.setParentCategory(fruitCategory); fruitCategory.getChildCategories().add(orangeCategory); orangeCategory.setParentCategory(fruitCategory); vegetableCategory.getChildCategories().add(tomatoCategory); tomatoCategory.setParentCategory(vegetableCategory); session.save(foodCategory); // 级联保存所有的关联对象 tx.commit(); } catch (Exception ex) { if ( null != tx) tx.rollback(); ex.printStackTrace(); } finally { if ( null != session) session.close(); } } public static void deleteCategoryWithCascade() throws Exception { Session session = sessionFactory.openSession(); Transaction tx = null ; try { tx = session.beginTransaction(); // session.get和session.load方法完成的功能是一样的,接受参数类型也是一样的,它们之间的唯一差别在于根据主键寻找某一个对象 // 如果找不到,get方法返回一个null,而load方法直接抛异常。 Category category = (Category)session.load(Category. class , new Long( 1 )); session.delete(category); // 级联保存所有的关联对象 tx.commit(); } catch (Exception ex) { if ( null != tx) tx.rollback(); ex.printStackTrace(); } finally { if ( null != session) session.close(); } } public static void main(String[] args) throws Exception { // saveCustomerAndOrderWithCascade(); // saveCategoryWithCascade(); deleteCategoryWithCascade(); } } |
最后在主配置文件hibernate.cfg.xml中增加对Category.hbm.xml文件的映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version= '1.0' encoding= 'UTF-8' ?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" > <!-- Generated by MyEclipse Hibernate Tools. --> <hibernate-configuration> <session-factory> <property name= "show_sql" > true </property> <!-- 属性之间没有上下关系,放在哪里都行 --> <property name= "connection.url" >jdbc:mysql: //localhost:3306/myhibernate3</property> <property name= "connection.username" >root</property> <property name= "connection.password" >root</property> <property name= "connection.driver_class" >com.mysql.jdbc.Driver</property> <property name= "dialect" >org.hibernate.dialect.MySQLDialect</property> <mapping resource= "Customer.hbm.xml" /> <!-- 将主配置文件包含对象-关系映射文件,之所以映射是因为hibernate启动时只会加载主配置文件 --> <mapping resource= "Order.hbm.xml" /> <mapping resource= "Category.hbm.xml" /> </session-factory> </hibernate-configuration> |
运行Test.java程序,可以看到保存和删除都已经实现了级联操作。
下面我们来看如何用hibernate表示一对一和多对多关系:
一对一在实际开发中用的也比较多,比如一个人对应一个身份证。这种一对一的关系可以使用共用主键来表达。首先导入工程hibernate1(风中叶银行企陪day6)
Student 和 Certificate 类及其相关的映射文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | package model; public class Student { private String id; // 标识id private String cardId; // 学号 private String name; // 学生姓名 private int age; // 岁数 private Certificate cer; // 身分证 private Team team; // 班级 public int getAge() { return age; } public String getName() { return name; } public String getCardId() { return cardId; } private void setId(String id) { this .id = id; } public void setAge( int age) { this .age = age; } public void setName(String stuName) { this .name = stuName; } public void setCardId(String cardId) { this .cardId = cardId; } public String getId() { return id; } public Student() { // 无参的构造函数 } public Certificate getCer() { return cer; } public void setCer(Certificate pass) { this .cer = pass; } /** * @return 返回 team。 */ public Team getTeam() { return team; } /** * @param team * 要设置的 team。 */ public void setTeam(Team team) { this .team = team; } } ---------------------------------------------------------------------------- package model; public class Certificate { private String id; private String describe; private Student stu; /** * @return 返回 stu。 */ public Student getStu() { return stu; } /** * @param stu * 要设置的 stu。 */ public void setStu(Student stu) { this .stu = stu; } /** * @return 返回 describe。 */ public String getDescribe() { return describe; } /** * @param describe * 要设置的 describe。 */ public void setDescribe(String describe) { this .describe = describe; } /** * @return 返回 id。 */ public String getId() { return id; } /** * @param id * 要设置的 id。 */ private void setId(String id) { this .id = id; } } ----------------------------------------------------------------- Student.hbm.xml: <?xml version= "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "model.Student" table= "student" lazy= "true" ><!--把类和数表关联起来--> <id name= "id" unsaved-value= "null" ><!--id的产生方式是uuid.hex--> <generator class = "uuid.hex" /> <!-- 或者写uuid也可以 --> </id> <property name= "cardId" type= "string" /><!--映射号--> <property name= "name" type= "string" /><!--映射学生名--> <property name= "age" type= "int" /><!--映射学生岁数--> <one-to-one name= "cer" class = "model.Certificate" fetch= "join" cascade= "all" /><!--映射对应的身分证对象--> </ class > </hibernate-mapping> ---------------------------------------------------------------------------------------------------------- Certificate.hbm.xml: <?xml version= "1.0" encoding= "GB2312" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "model.Certificate" table= "certificate" lazy= "true" > <id name= "id" > <generator class = "foreign" > <param name= "property" >stu</param> <!-- stu是Certificate类的一个成员变量 --> </generator> </id> <!-- describe是数据库中的一个保留字,不能作为字段名,因此需要加上``(键盘 1 左边的) --> <property name= "describe" column= "`describe`" type= "string" /> <one-to-one name= "stu" class = "model.Student" fetch= "select" constrained= "true" cascade= "none" /> <!-- cascade设置为none说明身份证丢(删除操作)了不能说明人没了:~ --> </ class > </hibernate-mapping> |
schema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | drop database if exists schoolproject; create database schoolproject; use schoolproject; drop table if exists certificate; CREATE TABLE certificate ( id varchar( 100 ) NOT NULL default '' , `describe` varchar( 100 ) default '' , PRIMARY KEY (id) ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; -- -- Dumping data for table 'certificate' -- INSERT INTO certificate VALUES ( 'ff80808105416d3b0105416d3eca0001' , 'tomclus' ); INSERT INTO certificate VALUES ( 'ff808081054175b501054175b9190001' , 'tom' ); -- -- Table structure for table 'student' -- drop table if exists student; CREATE TABLE student ( id varchar( 100 ) NOT NULL default '' , name varchar( 20 ) default '' , `cardId` varchar( 20 ) NOT NULL default '' , age int ( 11 ) default '0' , PRIMARY KEY (id) ) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci; -- -- Dumping data for table 'student' -- INSERT INTO student VALUES ( 'ff80808105416d3b0105416d3eca0001' , 'tomclus' , '200512345' , 33 ); INSERT INTO student VALUES ( 'ff808081054175b501054175b9190001' , 'tom' , '11111111' , 33 ); |
BM.java(business manager)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package BusinessManager; import model.Certificate; import model.Student; import org.hibernate.HibernateException; import persistence.StudentDAO; public class BM { public static void main(String[] args) throws HibernateException { Student stu = new Student(); stu.setName( "spark" ); stu.setCardId( "200211332" ); stu.setAge( 33 ); Certificate cer = new Certificate(); cer.setDescribe( "spark" ); //设定学生与身份证之间的关联关系 stu.setCer(cer); cer.setStu(stu); StudentDAO.saveObj(stu); } } |
下面是与之相关的类BaseDAO、StudentDAO、HibernateUtil:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | package persistence; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; /** * @author Administrator * * TODO 要更改此生成的类型注释的模板,请转至 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ public class BaseDAO { static Session session = null ; static Transaction tx = null ; /*------------创建新对象-----------------*/ public static void saveObj(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 session.save(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { if (session != null ) HibernateUtil.closeSession(session); } } /*------------删除对象-----------------*/ public static void delObject(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 Transaction tx = session.beginTransaction(); // 开启事务 session.delete(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } /*------------修改对象-----------------*/ public static void updateObj(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 Transaction tx = session.beginTransaction(); // 开启事务 session.update(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /* * 创建日期 2005-7-2 * * TODO 要更改此生成的文件的模板,请转至 * 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ package persistence; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Iterator; import java.util.List; import model.Student; import org.hibernate.FlushMode; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.Transaction; /** * @author Administrator * * TODO 要更改此生成的类型注释的模板,请转至 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ public class StudentDAO extends BaseDAO { static Session session = null ; static Transaction tx = null ; public static void update() { Student stu = null ; try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 Query query = session.getNamedQuery( "queryStudent_byAgeAdnName" ); query.setInteger( "minAge" , 25 ); // 设置“:”号后的minAge变量值 query.setString( "likeName" , "%clus%" ); // 设置“:”号后的likeName变量值 List list = query.list(); for ( int i = 0 ; i < list.size(); i++) { stu = (Student) list.get(i); System.out.println(stu.getName()); } tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package persistence; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure() .buildSessionFactory(); } catch (HibernateException ex) { throw new RuntimeException( "Exception building SessionFactory: " + ex.getMessage(), ex); } } public static Session currentSession() { Session s = sessionFactory.openSession(); return s; } public static void closeSession(Session s) { s.close(); } } |
引入MySql驱动,执行BM.java方法,执行一次保存操作后表中存储的数据有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | mysql> select * from student; +----------------------------------+---------+-----------+-----+ | id | name | cardId | age | +----------------------------------+---------+-----------+-----+ | 40281f815004a9ef015004a9f0860001 | spark | 200211332 | 33 | | ff80808105416d3b0105416d3eca0001 | tomclus | 200512345 | 33 | | ff808081054175b501054175b9190001 | tom | 11111111 | 33 | +----------------------------------+---------+-----------+-----+ 3 rows in set mysql> select * from certificate; +----------------------------------+----------+ | id | describe | +----------------------------------+----------+ | 40281f815004a9ef015004a9f0860001 | spark | | ff80808105416d3b0105416d3eca0001 | tomclus | | ff808081054175b501054175b9190001 | tom | +----------------------------------+----------+ 3 rows in set mysql> |
可以看到一对一的主键关联已经成功实现了。
一对一的第二种实现方式:其实还是通过外键来关联的(这种实现方式实际上就是退化了的一对多关联)。重命名当前工作空间下的hibernate2项目,导入风中叶老师的hibernate2(day6),可以看到Student.java、Certificate.java、Student.hbm.xml都没用任何变换,唯一变化的是id的生成方式,和删除了one-to-one标签增加了many-to-one标签。
Certificate.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version= "1.0" encoding= "GB2312" ?> <!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "model.Certificate" table= "certificate" lazy= "true" > <id name= "id" > <generator class = "uuid.hex" /> </id> <property name= "describe" column= "`describe`" type= "string" /> <many-to-one name= "stu" class = "model.Student" unique= "true" <!-- 多对一,而多的一方又是唯一的,暗示着多对一已经退化成了一对一了 --> column= "stu_id" <!-- 外键 --> /> <!-- 唯一的多对一,实际也就变成一对一关系了 --> </ class > </hibernate-mapping> |
数据库schema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | drop database if exists schoolproject; create database schoolproject; use schoolproject; -- -- Table structure for table `certificate` -- DROP TABLE IF EXISTS `certificate`; CREATE TABLE `certificate` ( `id` varchar( 100 ) NOT NULL default '' , `describe` varchar( 100 ) default '' , `stu_id` varchar( 32 ) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Dumping data for table `certificate` -- /*!40000 ALTER TABLE `certificate` DISABLE KEYS */ ; LOCK TABLES `certificate` WRITE; INSERT INTO `certificate` VALUES ( '5abfa70605c5356f0105c53573360002' , 'tomclus' , '5abfa70605c5356f0105c535730e0001' ); INSERT INTO `certificate` VALUES ( '5abfa70605c535a60105c535aa370002' , 'tom' , '5abfa70605c535a60105c535aa040001' ); UNLOCK TABLES; /*!40000 ALTER TABLE `certificate` ENABLE KEYS */ ; -- -- Table structure for table `student` -- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` varchar( 100 ) NOT NULL default '' , `name` varchar( 20 ) default '' , `cardId` varchar( 20 ) NOT NULL default '' , `age` int ( 11 ) default '0' , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Dumping data for table `student` -- /*!40000 ALTER TABLE `student` DISABLE KEYS */ ; LOCK TABLES `student` WRITE; INSERT INTO `student` VALUES ( '5abfa70605c5356f0105c535730e0001' , 'tomclus' , '200212345' , 33 ); INSERT INTO `student` VALUES ( '5abfa70605c535a60105c535aa040001' , 'tom' , '200254321' , 33 ); UNLOCK TABLES; |
执行BM.java,里面的代码跟之前的一模一样。可以看到certificate表的stu_id已经和student表的id关联起来了。
下面我们来看相对来说最复杂的一种:多对多的映射类型
重命名当前项目下的hibernate3,导入风中叶老师的hibernate3(day6),我们看一下学生和课程之间的多对多通过hibernate如何实现。
多对多在程序中如何体现:两个类Student、Course,分别定义两个集合类型的变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | package model; import java.util.Set; public class Student { private String id; // 标识id private String cardId; // 学号 private String name; // 学生姓名 private int age; // 岁数 private Set Courses; // 课程 public int getAge() { return age; } public String getName() { return name; } public String getCardId() { return cardId; } private void setId(String id) { this .id = id; } public void setAge( int age) { this .age = age; } public void setName(String stuName) { this .name = stuName; } public void setCardId(String cardId) { this .cardId = cardId; } public String getId() { return id; } public Student() { // 无参的构造函数 } /** * @return 返回 courses。 */ public Set getCourses() { return Courses; } /** * @param courses * 要设置的 courses。 */ public void setCourses(Set courses) { Courses = courses; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | package model; import java.util.HashSet; import java.util.Set; public class Course { private String id; private String name; private Set Students = new HashSet(); /** * @return 返回 id。 */ public String getId() { return id; } /** * @param id * 要设置的 id。 */ public void setId(String id) { this .id = id; } /** * @return 返回 name。 */ public String getName() { return name; } /** * @param name * 要设置的 name。 */ public void setName(String name) { this .name = name; } /** * @return 返回 students。 */ public Set getStudents() { return Students; } /** * @param students * 要设置的 students。 */ public void setStudents(Set students) { Students = students; } } |
而在数据库中要想体现多对多的关系,就需要使用连接表,连接表中的内容就是stu_id和course_id。一条这样的记录,就表示一个映射:该学生选择了该课程,该课程被该学生所选。
Student.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?xml version= "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "model.Student" table= "student" select-before-update= "true" ><!--把类和数表关联起来--> <id name= "id" unsaved-value= "null" ><!--id的产生方式是uuid.hex--> <generator class = "uuid.hex" /> </id> <property name= "cardId" type= "string" /><!--映射号--> <property name= "name" type= "string" /><!--映射学生名--> <property name= "age" type= "int" /><!--映射学生岁数--> <!-- 如果不设置inverse为 true 的话,会抛出约束违规异常,Duplicate entry。因为inverse的默认值为 false ,表示自己维持级联关系,这样在执行save操作时,student和course都要维持(向表中插入一条记录)就会抛异常 --> <!-- cascade绝对不能设为all或者delete,因为删除课程不能删除学生,删除了一个学生也不应该把这个学生的课程删除 --> <set name= "courses" table= "student_course" cascade= "none" inverse= "true" > <key column= "stu_id" /> <!-- 字段stu_id代表中间表student_course中的字段 --> <many-to-many class = "model.Course" column= "course_id" /> <!-- 字段course_id代表中间表student_course中的字段 --> </set> </ class > </hibernate-mapping> |
Course.hbm.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version= "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> < class name= "model.Course" table= "course" select-before-update= "true" ><!--把类和数表关联起来--> <id name= "id" unsaved-value= "null" ><!--id的产生方式是uuid.hex--> <generator class = "uuid.hex" /> </id> <property name= "name" type= "string" /><!--映射课程名--> <set name= "students" table= "student_course" cascade= "save-update" > <key column= "course_id" /> <many-to-many class = "model.Student" column= "stu_id" /> </set> </ class > </hibernate-mapping> |
数据库schema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | drop database if exists schoolproject; create database schoolproject; use schoolproject; -- -- Table structure for table `course` -- DROP TABLE IF EXISTS `course`; CREATE TABLE `course` ( `id` varchar( 32 ) NOT NULL default '' , `name` varchar( 45 ) default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Dumping data for table `course` -- /*!40000 ALTER TABLE `course` DISABLE KEYS */ ; LOCK TABLES `course` WRITE; INSERT INTO `course` VALUES ( '5abfe4c705ca8ee00105ca8ee45d0002' , 'history' ); INSERT INTO `course` VALUES ( '5abfe4c705ca8f5e0105ca8f62400002' , 'computer' ); INSERT INTO `course` VALUES ( '5abfe4c705ca8faf0105ca8fb3750002' , 'music' ); INSERT INTO `course` VALUES ( '5abfe4c705ca901f0105ca9024290002' , 'ecnomic' ); INSERT INTO `course` VALUES ( '5abfe4c705ca98420105ca98475a0001' , 'politics' ); UNLOCK TABLES; /*!40000 ALTER TABLE `course` ENABLE KEYS */ ; -- -- Table structure for table `student` -- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` varchar( 100 ) NOT NULL default '' , `name` varchar( 20 ) default '' , `cardId` varchar( 20 ) NOT NULL default '' , `age` int ( 11 ) default '0' , PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Dumping data for table `student` -- /*!40000 ALTER TABLE `student` DISABLE KEYS */ ; LOCK TABLES `student` WRITE; INSERT INTO `student` VALUES ( '5abfe4c705ca8ee00105ca8ee42b0001' , 'tomclus' , '1' , 25 ); INSERT INTO `student` VALUES( '5abfe4c705ca8f5e0105ca8f620d0001' , 'tom' , '2' , 25 ); INSERT INTO `student` VALUES( '5abfe4c705ca8faf0105ca8fb3390001' , 'spark' , '3' , 25 ); INSERT INTO `student` VALUES( '5abfe4c705ca901f0105ca9023f70001' , 'jerry' , '4' , 25 ); UNLOCK TABLES; /*!40000 ALTER TABLE `student` ENABLE KEYS */ ; -- -- Table structure for table `student_course` -- DROP TABLE IF EXISTS `student_course`; CREATE TABLE `student_course` ( `stu_id` varchar( 32 ) NOT NULL default '' , `course_id` varchar( 32 ) NOT NULL default '' , PRIMARY KEY (`stu_id`,`course_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Dumping data for table `student_course` -- /*!40000 ALTER TABLE `student_course` DISABLE KEYS */ ; LOCK TABLES `student_course` WRITE; INSERT INTO `student_course` VALUES ( '5abfe4c705ca8ee00105ca8ee42b0001' , '5abfe4c705ca8ee00105ca8ee45d0002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8ee00105ca8ee42b0001' , '5abfe4c705ca8f5e0105ca8f62400002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8ee00105ca8ee42b0001' , '5abfe4c705ca8faf0105ca8fb3750002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8f5e0105ca8f620d0001' , '5abfe4c705ca8f5e0105ca8f62400002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8f5e0105ca8f620d0001' , '5abfe4c705ca901f0105ca9024290002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8faf0105ca8fb3390001' , '5abfe4c705ca8f5e0105ca8f62400002' ); INSERT INTO `student_course` VALUES ( '5abfe4c705ca8faf0105ca8fb3390001' , '5abfe4c705ca8faf0105ca8fb3750002' ); UNLOCK TABLES; |
相关类BM.java、StudentDAO.java、BaseDAO、HibernateUtil.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package BusinessManager; import org.hibernate.HibernateException; import persistence.StudentDAO; public class BM { public static void main(String[] args) throws HibernateException { StudentDAO.mdfChoice(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /* * 创建日期 2005-7-2 * * TODO 要更改此生成的文件的模板,请转至 * 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ package persistence; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import model.Course; import model.Student; import org.hibernate.FlushMode; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.Transaction; /** * @author Administrator * * TODO 要更改此生成的类型注释的模板,请转至 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ public class StudentDAO extends BaseDAO { static Session session = null ; static Transaction tx = null ; public static void mdfChoice() { Set set = new HashSet(); Student stu = null ; Course course = null ; try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 stu = (Student) session.createQuery( "from Student s where s.name ='tomclus'" ).uniqueResult(); course = (Course) session.createQuery( "from Course c where c.name='ecnomic'" ).uniqueResult(); stu.getCourses().add(course); course.getStudents().add(stu); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | /* * 创建日期 2005-7-2 * * TODO 要更改此生成的文件的模板,请转至 * 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ package persistence; import model.Student; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; /** * @author Administrator * * TODO 要更改此生成的类型注释的模板,请转至 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ public class BaseDAO { static Session session = null ; static Transaction tx = null ; /*------------创建新对象-----------------*/ public static void createObj(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 session.save(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { if (session != null ) HibernateUtil.closeSession(session); } } /*------------删除对象-----------------*/ public static void delObject(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 session.delete(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } /*------------修改对象-----------------*/ public static void mdfObj(Object o) { try { session = HibernateUtil.currentSession(); // 开启连接 tx = session.beginTransaction(); // 开启事务 session.update(o); tx.commit(); } catch (HibernateException e) { // 捕捉例外 e.printStackTrace(); tx.rollback(); } finally { HibernateUtil.closeSession(session); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package persistence; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure() .buildSessionFactory(); } catch (HibernateException ex) { throw new RuntimeException( "Exception building SessionFactory: " + ex.getMessage(), ex); } } public static Session currentSession() { Session s = sessionFactory.openSession(); return s; } public static void closeSession(Session s) { s.close(); } } |
执行BM.java类,学生表和课程表没有任何变化,唯一变化的是中间表多了一行记录,用于映射新建立的学生和课程之间的关系。
补充知识点:域对象在持久化层的三种状态
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· dotnet 源代码生成器分析器入门
· 官方的 MCP C# SDK:csharp-sdk
· 一款 .NET 开源、功能强大的远程连接管理工具,支持 RDP、VNC、SSH 等多种主流协议!
· 一步一步教你部署ktransformers,大内存单显卡用上Deepseek-R1
· 一次Java后端服务间歇性响应慢的问题排查记录