• 第二十一部分_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信息:

    <?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即二进制大型物件)

    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:

    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:

    <?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文件:

    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();
    	}
    
    }
    

    通过给一下几行代码添加注释的方式进行测试:

    		saveCustomer(customer);
    
    		findAllCustomers(out);
    
    		loadAndUpdateCustomer(customer.getId(), "Tianjin");
    
    		findAllCustomers(out);
    
    		deleteAllCustomers();
    

    运行后在hibernate2根目录下面会生成一个photo_copy.gif图形文件。

    上面的例子比较简单,下面我们看一个复杂的:表与表之间存在关联关系,类与类之间存在关联关系:

    新建一个名为hibernate3的Java Project,导入相应的hibernate包(操作过程如之前所述)。

    配置hibernate.cfg.xml:

    <?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:

    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类:

    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:

    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:

    <?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:

    <?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:

    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输出:

    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:

    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:

    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文件:

    <?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:

    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文件的映射:

    <?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 类及其相关的映射文件

    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:

    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) 

    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:

    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);
    		}
    
    	}
    }
    
    /*
     * 创建日期 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);
    		}
    
    	}
    
    }
    
    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方法,执行一次保存操作后表中存储的数据有:

    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:

    <?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:

    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,分别定义两个集合类型的变量:

    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;
    	}
    }
    
    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:

    <?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:

    <?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:

    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:

    package BusinessManager;
    
    import org.hibernate.HibernateException;
    
    import persistence.StudentDAO;
    
    public class BM
    {
    	public static void main(String[] args) throws HibernateException
    	{
    
    		StudentDAO.mdfChoice();
    
    	}
    
    }
    
    /*
     * 创建日期 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);
    		}
    
    	}
    
    }
    
    /*
     * 创建日期 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);
    		}
    
    	}
    }
    
    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类,学生表和课程表没有任何变化,唯一变化的是中间表多了一行记录,用于映射新建立的学生和课程之间的关系。

    补充知识点:域对象在持久化层的三种状态

  • 相关阅读:
    SQLServer 知识点
    Entity转换为ViewModel时提供的一种转换方法
    Linq中IGrouping转换为IQueryable
    封装整形属性时对应到枚举
    新的转换列表方式
    工作态度
    EasyFrame
    NewCloud
    将博客搬至CSDN
    Html的语义化
  • 原文地址:https://www.cnblogs.com/Code-Rush/p/4833595.html
Copyright © 2020-2023  润新知