单向N-1关联
单向N-1关系,比如多个人对应一个地址,只需从人实体端可以找到对应的地址实体,无须关系某个地址的全部住户。 单向N-1关联只需从N的一端可以访问1的一端。
域模型
从Order到Customer的多对一单向关联需要在Order类中定义一个Customer属性,而在Customer类中无需定义存放Order对象的集合属性。即一个顾客可以对应多个订单。
关系数据模型
ORDERS 表中的CUSTOMER_ID 参照CUSTOMER 表的主键。
配置关系映射文件(Order.hbm.xml)
many-to-one指明了外键 ,会根据反射机制,找到要和ORDER建立多对一关系的类,该列默认的是可以为空的
<many-to-one name="customer" column="CUSTOMER_ID" class="mypacker.entity.Customer"></many-to-one>
映射多对一的关联关系,使用many-to-one 来映射多对一的关联关系
name:多这一端关联的一那一端的属性的名字 class:一那一端的属性对应的类名
column:一那一端在多的一端对应的数据表中的外键的名字
部门的类型
配置持久化类:
public class Order { private Integer orderId; private String orderName; private Customer customer; //单向多对一(如果那个类还有一个对此类的一对多,则这个就是双向的多对一) }
save()方法
public void testMany2OneSave(){ Customer customer = new Customer(); customer.setCustomerName("AA"); 在Customer表中创建一个CustomerName=AA的数据,ID自动生成 Order order1 = new Order(); order1.setOrderName("Order-1");//在Order表中建立Order-1、Order-2两条数据,ID也是自动生成,外键Customer_ID等于上面的AA那条数据的ID Order order2 = new Order(); order2.setOrderName("Order-2"); //设定关联关系 //为两条Order对象添加一个Customer对象信息 order1.setCustomer(customer); order2.setCustomer(customer); //执行save操作:推荐先插入1的一端,再插入n的一端 //如果先插入Order,再插入Customer,会产生3条INSERT,2条UPDATE //因为在插入多的一端时,无法确定1的一端的外键值,所以只能等1的一端插入后,再额外发送UPDATE语句 session.save(customer); session.save(order1); session.save(order2); transaction.commit(); }
Hibernate: insert into CUSTOMERS (CUSTOMER_NAME) values ('AA') ##CUSTOMER_ID为1## Hibernate: insert into ORDERS (ORDER_NAME, CUSTOMER_ID) values (Order-1, 1) Hibernate: insert into ORDERS (ORDER_NAME, CUSTOMER_ID) values (Order-2, 1)
get()方法
public void testMany2OneGet(){ //1.若查询多的一端的对象,则默认情况下,只查询了多的一端的对象,而没有查询关联的一的一端的对象 //获取Order对象时,默认情况下,其关联的Customer对象是一个代理对象 Order order = (Order) session.get(Order.class,5); System.out.println(order.getOrderName()); //session.close(); //2.当在需要使用到关联对象的属性时,才发送对应的SQL查询语句 Customer customer = new Customer(); System.out.println(customer.toString()); //3.在查询Customer对象时,由多的一端导航一的一端时,若此时session已被关闭,则会发生懒加载异常 }
控制台打印出SQL语句:
Hibernate: select order0_.ORDER_ID as ORDER1_1_0_, order0_.ORDER_NAME as ORDER2_1_0_, order0_.CUSTOMER_ID as CUSTOMER3_1_0_ from ORDERS order0_ where order0_.ORDER_ID=?
update()方法
public void testMany2OneUpdate(){ Order order = (Order) session.get(Order.class,5); order.getCustomer().setCustomerName("AAA"); //通过orderId=5的那条记录获取对应的customer,而数据库中的那条customer的id=3,所以,costumer表中id=3的那条记录的name变更为AAA session.update(order); transaction.commit(); }
控制台打印出SQL语句:
Hibernate: select order0_.ORDER_ID as ORDER1_1_0_, order0_.ORDER_NAME as ORDER2_1_0_, order0_.CUSTOMER_ID as CUSTOMER3_1_0_ from ORDERS order0_ where order0_.ORDER_ID=5 Hibernate: select customer0_.CUSTOMER_ID as CUSTOMER1_0_0_, customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_ from CUSTOMERS customer0_ where customer0_.CUSTOMER_ID=? Hibernate: update CUSTOMERS set CUSTOMER_NAME=? where CUSTOMER_ID=?
delete()方法
public void testMany2OneDelete(){ Customer customer = (Customer) session.get(Customer.class,3); //在不设定关联关系的情况下,且1这一端的对象有n的对象在引用,则不能直接删除1这一端的对象 //也就是说在CUSTOMER表的主键数据在ORDER表中有与之对应的外键数据,如果删掉了CUSTOMER的主键=1的记录,那ORDER表中的外键=1的记录就没法与主表对应了,所以会出错,得先删掉ORDER表中的外键=1的记录,才能删掉CUSTOMER的主键=1的记录 //org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update session.delete(customer); transaction.commit(); }
双向1-N关联
对于1-N关联,Hibernate推荐使用双向关联,而且不要让1的一端控制关联关系,而使用N的一端控制关联关系。 双向的N-1关联与1-N关联是完全相同的两种情况。两端都需要增加对关联属性的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。
域模型
从Order到Customer的多对一双向关联需要在Order类中定义一个Customer属性,而在Customer类中需定义存放Order对象的集合属性。
关系数据模型
ORDERS表中的CUSTOMER_ID参照CUSTOMER表的主键。
配置关系映射文件
<set name="orders" inverse="true" cascade="delete" order-by="ORDER_NAME DESC"> <key column="CUSTOMER_ID"></key> <!-- key指明了ORDER表中的外键 --> <one-to-many class="mypacker.entity.Order"></one-to-many><!-- one-to-many指明了和哪个类进行一对多的映射 --> </set>
name:多这一端关联的一那一端的属性的名字
column:一那一端在多的一端对应的数据表中的外键的名字
class:一那一端的属性对应的类名
Hibernate_set的3个属性
① <set>元素的inverse属性
在Hibernate中通过对inverse属性的设置来决定是由双向关联的哪一方来维护表和表之间的关系,inverse=false的为主动方,inverse=true的为被动方,由主动方负责维护关联关系。在没有设置inverse=true的情况下,父子两边都维护父子关系。
② <set>、<many-to-one>、<one-to-many>都有一个cascade属性,它用于设定级联操作,开发时不建议设定该属性。常用的取值包括:
save-update:当通过Session的save()、update()、saveOrUpdate()方法来保存或更新当前对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象。
delete:当通过Session的delete()方法删除当前对象时,会级联删除所有关联的对象。
delete-orphan:删除所有和当前对象接触关联关系的对象。
③ <set>元素的order-by属性,如果设置了该属性,当Hibernate通过selete语句到数据库中检索集合对象时,利用order by子句进行排序。order-by属性中还可以加入SQL函数。order-by 中使用的是表的字段名,而不是持久化类的属性名。
配置持久化类
public class Customer { private Integer customerId; private String customerName; //需要把集合进行初始化防止空指针异常 //声明集合类型时,需使用接口类型,因为Hibernate在获取集合类型时,返回的是Hibernate内置的集合类型,而不是JavaSE一个标准的集合实现 private Set<Order> orders = new HashSet<Order>(); //双向一对多
save()方法
public void testOne2ManySave(){ Customer customer = new Customer(); customer.setCustomerName("GG"); //在Customer表中创建一个CustomerName=GG的数据,ID自动生成 Order order1 = new Order(); order1.setOrderName("Order-3"); //在Order表中建立Order-3、Order-4两条数据,ID也是自动生成,外键Customer_ID等于上面的GG那条数据的ID Order order2 = new Order(); order2.setOrderName("Order-4"); //设定关联关系 order1.setCustomer(customer); order2.setCustomer(customer); //为当前Customer对象添加两条Order信息 customer.getOrders().add(order1); customer.getOrders().add(order2); //执行save操作:先插入Customer,再插入Order,3条INSERT语句,2条UPDATE语句 //因为1的一端和n的一端都维护关联关系所以会多出UPDATE //可以在1的一端的set节点指定inverse=true来使1的一端放弃维护关联关系 //建议设定set的inverse=true,建议先插入1的一端,后插入多的一端,好处是不会多出UPDATE语句 session.save(customer); session.save(order1); session.save(order2); transaction.commit(); }
get()方法
public void testOne2ManyGet(){ //对 n 的一端的集合使用延迟加载,在使用集合中的元素时才初始化,所以也会可能抛出懒加载异常 Customer customer = (Customer) session.get(Customer.class,1); //返回的多的一端的集合是Hibernate内置的集合类型,该类型具有延迟加载和存放代理对象功能 System.out.println(customer.getCustomerName()); }
update()方法
public void testOne2ManyUpdate(){ Customer customer = (Customer) session.get(Customer.class,4); customer.getOrders().iterator().next().setOrderName("GGG"); //把外键CUSTOMER_ID=4的那条数据对应的ORDER_NAME改为GGG,但只是第一条改了 session.save(customer); transaction.commit(); }
delete()方法
public void testOne2ManyDelete(){ Customer customer = (Customer) session.get(Customer.class,4); //与多对一一样,在不设定关联关系的情况下,且1这一端的对象有n的对象在引用,则不能直接删除1这一端的对象 //也就是说在CUSTOMER表的主键数据在ORDER表中有与之对应的外键数据, //如果删掉了CUSTOMER的主键=1的记录,那ORDER表中的外键=1的记录就没法与主表对应了, //所以会出错,得先删掉ORDER表中的外键=1的记录,才能删掉CUSTOMER的主键=1的记录 session.delete(customer); }
HQL: Hibernate查询语言
HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似,在Hibernate提供的各种检索方式中,HQL是使用最广的一种检索方式。
HQL查询包含以下步骤:
1、获取Hibernate Session对象。
2、编写HQL语句。
3、以HQL语句作为参数,调用Session的createQuery方法创建查询对象。
4、如果HQL语句包含参数,则调用Query的setXxx方法为参数赋值。
5、调用Query对象的list()或uniqueResult()方法返回查询结果列表。
public class UserTest { Session session; //junit测试,先执行before,后内容,最后执行after,这样减少重复代码量 @Before public void before(){ Configuration configuration = new Configuration().configure(); SessionFactory sessionFactory = configuration.buildSessionFactory( new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build()); session = sessionFactory.getCurrentSession(); //sessionFactory.openSession() session.beginTransaction(); } @After public void after(){ session.getTransaction().commit(); } @Test public void testAddUser() { User user = new User(1,"张三","abcd",new Date()); session.save(user); } @Test public void testSelectUserById(){ // User user = (User) session.get(User.class, 1); User user = (User) session.load(User.class, 1); System.out.println(user); } @Test public void testUpdate(){ // User user = new User(1,"李四1","123",new Date()); // session.update(user); User user = (User) session.get(User.class, 1); user.setUserName("王二"); session.update(user); } @Test public void testDelete(){ // User user = new User(1,"李四1","123",new Date()); // session.delete(user); User user = (User)session.get(User.class, 2); session.delete(user); } @Test public void testSelectAllUser(){ String hql = "FROM User ORDER BY id DESC "; Query query = session.createQuery(hql); List<User> userLists = query.list(); for(User user:userLists){ System.out.println(user); } } @Test public void testSelectUserName(){ String hql = "SELECT u.userName FROM User AS u"; Query query = session.createQuery(hql); List<String> userLists = query.list(); for(String user:userLists){ System.out.println(user); } } @Test public void testSelectdUserByUsername(){ String hql = "FROM User WHERE userName = :name AND passWord = :psw"; Query query = session.createQuery(hql); query.setParameter("name", "张三"); query.setParameter("psw", "abcd"); User user = (User) query.uniqueResult(); System.out.println(user); } @Test public void testUpdateUsers(){ String hql = "UPDATE User SET userName=:name WHERE passWord=:psw"; Query query = session.createQuery(hql); query.setParameter("name", "麻子"); query.setParameter("psw", "123"); int order = query.executeUpdate(); System.out.println("order :"+order); } @Test public void testDeleteUserByUserName(){ String hql = "DELETE User WHERE userName = :name"; Query query = session.createQuery(hql); query.setParameter("name", "张三"); query.executeUpdate(); } }