Hibernate第一天
1. 搭建Hibernate环境
-
搭建Hibernate开发环境
数据库:
DROP TABLE IF EXISTS `cst_customer`; CREATE TABLE `cst_customer` ( `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)', `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源', `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业', `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别', `cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址', `cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of cst_customer -- ---------------------------- INSERT INTO `cst_customer` VALUES ('94', '张三', null, null, null, null, null); INSERT INTO `cst_customer` VALUES ('95', '李四', '湖北', '湖北大学', null, null, null); INSERT INTO `cst_customer` VALUES ('96', '赵四', null, null, null, null, null); INSERT INTO `cst_customer` VALUES ('97', '赵四', null, null, null, null, null); INSERT INTO `cst_customer` VALUES ('98', '赵四', null, null, null, null, null); INSERT INTO `cst_customer` VALUES ('99', '赵四', null, null, null, null, null); INSERT INTO `cst_customer` VALUES ('100', 'aaaa', null, null, null, null, null); DROP TABLE IF EXISTS `cst_linkman`; CREATE TABLE `cst_linkman` ( `lkmId` bigint(20) NOT NULL AUTO_INCREMENT, `lkm_name` varchar(255) DEFAULT NULL, `lkm_gender` varchar(255) DEFAULT NULL, `lkm_phone` varchar(255) DEFAULT NULL, `lkm_mobile` varchar(255) DEFAULT NULL, `lkm_email` varchar(255) DEFAULT NULL, `lkm_position` varchar(255) DEFAULT NULL, `lkm_memo` varchar(255) DEFAULT NULL, `lkm_cust_id` int(11) DEFAULT NULL, PRIMARY KEY (`lkmId`), KEY `FKh9yp1nql5227xxcopuxqx2e7q` (`lkm_cust_id`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
导包:\hibernate-release-5.3.9.Final\lib\required
, 再添加连接数据的jar包
创建Customer.java实体对象
创建一个Customer.hbm(Hibernate mapping).xml,然后导约束,约束在 hibernate-core-5.3.9.Final.jar
中。
-
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="Customer" table="cst_customer"> <id name="custId" column="cust_id"> <!-- generator:是指定主键的生成方式。取值是固定的几个之中选一个 native是使用本地数据库的自动增长能力。 --> <generator class="native"></generator> </id> <property name="custName" column="cust_name"></property> <property name="custSource" column="cust_source"></property> <property name="custIndustry" column="cust_industry"></property> <property name="custLevel" column="cust_level"></property> <property name="custAddress" column="cust_address"></property> <property name="custPhone" column="cust_phone"></property> </class> </hibernate-mapping>
-
Hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- 配置SessionFactory SessionFactory的作用就是用于创建Session对象。 Session对象就是hibernate中操作数据库的核心对象。 此处的操作不要求背,但是要求记住创建SessionFactory必须的三部分信息 第一部分:连接数据库的信息 第二部分:hibernate的可选配置 第三部分:映射文件的位置 --> <session-factory> <!-- 第一部分:连接数据库的信息:可在hibernate包中查找相应的配置 \hibernate-release-5.3.9.Final\project\etc\hibernate.properties --> <!-- ## MySQL #hibernate.dialect org.hibernate.dialect.MySQLDialect #hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect #hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect #hibernate.connection.driver_class com.mysql.jdbc.Driver #hibernate.connection.url jdbc:mysql:///test #hibernate.connection.username gavin #hibernate.connection.password --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property><!-- 数据库方言 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jbdc:mysql://localhost:3306/spring</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 第二部分:hibernate的可选配置 --> <property name="hibernate.show_sql">true</property> <!-- 是否显示生成sql语句 --> <property name="hibernate.format_sql">true</property> <!-- 是否使用格式化生成sql语句 --> <!-- 配置hibernate采用何种方式来生产DDL语句 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- update表示检查实体类的映射配置和数据库的表结构是否一致,如果不一致则更改数据库表结构 --> <!-- SQL结构化查询语言:一共分为6个部分 DDL:Data Definition Language 数据定义语言(建库、建表、创建表结构修改表结构) DML:Data Manipulation Language 数据操纵语言(增、删、改) DQL:Data Query Language 数据查询语言 (查) DCL:Data Control Language 数据控制语言(授权) CCL:Cursor Control Language 游标控制语言 TPL: Transaction Processing Language 事务处理语言 --> <!-- 第三部分:配置映射文件的位置 --> <mapping resource="com/zwd/domain/Customer.hbm.xml"/> </session-factory> </hibernate-configuration>
2 Hibernate入门案例
测试:
package com.zwd.test;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import com.zwd.domain.Customer;
/**
* hibernate的入门案例(保存客户)
*
* @author Administrator
*
*/
public class Test1 {
/**
* 步骤: 1.解析hibernate的主配置文件 2.根据配置文件创建SessionFactory
* 3.根据SessionFactory创建Session 4.开始事务 5.执行操作 6.提交事务 7.释放资源
*/
@Test
public void test1() {
Customer customer = new Customer();
customer.setCustName("hibernate01");
// 1.解析hibernate的主配置文件
Configuration cfg = new Configuration();
cfg.configure();// 在内路径下加载配置文件 cfg.configure("Hibernate.cfg.xml");
// 2.根据配置文件创建SessionFactory
SessionFactory sessionFactory = cfg.buildSessionFactory();
// 3.根据SessionFactory创建Session
Session session = sessionFactory.openSession();
// 4.开始事务
Transaction transaction = session.beginTransaction();
// 5.执行操作
session.save(customer);
// 6.提交事务
transaction.commit();
// 7.释放资源
session.close();
sessionFactory.close();
}
}
出现的问题:https://blog.youkuaiyun.com/jerny2017/article/details/81982454
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near**‘type=MyISAM’ at line 10**
解决办法:更改数据库方言,指定mysql的版本 。MySQL5Dialect
<!-- 数据库方言 -->
<!--<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
3.Hibernate的常用对象
Configuration:
SessionFactory: 使用原则:一个应用应该只有一个SessionFactory,在应用加载时创建,应用卸载时销毁。
Session: 它是负责操作数据库,我们要掌握该对象中操作数据库的方法。
使用原则:一个线程只能有一个Session对象。单线程对象。
Transaction: 负责提交和回滚事务。
4.Hibernate CRUD操作
-
由于SessionFactory只在应用创建一次,所以需要写一个工具类来实现。
HibernateUtil.java
package com.zwd.utils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; /** * Hibernate的工具类 * 用于加载配置、创建一个应用的SessionFactory * * @author Administrator * */ public class HibernateUtil { private static SessionFactory sessionFactory ; //加载配置,并创建sessionFactory static{ Configuration cfg = new Configuration(); cfg.configure(); sessionFactory = cfg.buildSessionFactory(); } /** * 获取一个新的Session(一个线程一个Session) * @return */ public static Session getSession(){ return sessionFactory.openSession(); } }
-
hibernate的CRUD操作
保存: session.save(obj); 查询一个: session.get(class,primaryKey)、session.load(class,primaryKey) 更新: session.update(obj) 删除: session.delete(obj) 查询所有: 方式一: (不常用) SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer"); List<Object[]> list = sqlQuery.list(); for (Object[] os : list) { System.out.println("===========数组中的内容=========="); for (Object o : os) { System.out.println(o); } }
package com.zwd.test;
import java.util.List;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.zwd.domain.Customer;
import com.zwd.utils.HibernateUtil;
@SuppressWarnings("deprecation")
public class HibernateDemo1 {
/**
* 保存 session.save(obj);
*/
@Test
public void testSave() {
Customer c = new Customer();
c.setCustName("Hinernate02");
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//保存客户
session.save(c);
tx.commit();
session.close();
}
/**
* 查询一个:session.get(class,primaryKey)
*/
@Test
public void testFindOne() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//查询一个
Customer customer = session.get(Customer.class,6);
System.out.println(customer);
tx.commit();
session.close();
}
/**
* 更新:session.update(obj)
*/
@Test
public void testUpdate() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class,6);
customer.setCustAddress("贵州遵义");
//更新
session.update(customer);
tx.commit();
session.close();
}
/**
* 删除:session.delete(obj)
*/
@Test
public void testDelete() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 6);
//删除
session.delete(customer);
tx.commit();
session.close();
}
@Test
public void testFindAll() {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//之后不用这个,用session.createQuery(arg0)
SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = sqlQuery.list();
for (Object[] os : list) {
System.out.println("===========数组中的内容==========");
for (Object o : os) {
System.out.println(o);
}
}
}
}
5.在hibernate中配置连接池
如果没有使用连接池就是使用的是原始的JDBC
-
导入c3p0的jar包
\hibernate-release-5.3.9.Final\lib\optional\c3p0
-
在hibernate的配置文件中添加配置
可在hibernate包中查找相应的配置
\hibernate-release-5.3.9.Final\project\etc\hibernate.properties
<!-- 配置c3p0连接池 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
/** * hibernate中 如何使用原始JDBC API * JDBC中的API: * Connection * Statement * PreparedStatement * ResultSet */ @Test public void testc3p0(){ //1.获取session对象 Session session = HibernateUtil.getSession(); //2.调用doWork方法 session.doWork(new Work() { @Override public void execute(Connection arg0) throws SQLException { System.out.println(arg0); } }); }
6.Hibrnate 查询一个的两种方式的异同(get()、load())
Hibernate中查询一个的方法
get方法:
get(Class clazz,Serialized id);
load方法:
load(Class clazz,Serialized id);
共同点:都是根据id查询一个实体
区别:
1、查询的时机不一样。
get的查询时机:每次调用get方法时,马上发起查询。(立即加载)
load的查询时机:每次真正使用的时候,发起查询。 (延迟加载、懒加载、惰性加载)
2、返回结果不一样。
get方法返回的对象是实体对象。
load方法返回的对象是实体类类型的代理对象。
load方法默认情况下是延迟,但是可以通过配置的方式改为立即加载。(Hibernate第三天再讲)
/**
* Hibernate中查询一个的方法
* get方法:
* get(Class clazz,Serialized id);
* load方法:
* load(Class clazz,Serialized id);
* 共同点:都是根据id查询一个实体
* 区别:
* 1、查询的时机不一样。
* get的查询时机:每次调用get方法时,马上发起查询。(立即加载)
* load的查询时机:每次真正使用的时候,发起查询。 (延迟加载、懒加载、惰性加载)
* 2、返回结果不一样。
* get方法返回的对象是实体对象。
* load方法返回的对象是实体类类型的代理对象。
* load方法默认情况下是延迟,但是可以通过配置的方式改为立即加载。(Hibernate第三天再讲)
*/
@Test
public void testGet(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 5);
System.out.println("get: " + customer);
tx.commit();
session.close();
}
@Test
public void testLoad(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Customer customer = session.load(Customer.class, 5);
System.out.println("load : "+ customer);
tx.commit();
session.close();
}
Hibernate第二天
1.实体类的编写规范
应该遵循JavaBean的编写规范。
Bean:在软件开发中指的是可重用的组件。
JavaBean:指的是用java语言编写的可重用的组件。在实际项目中:domain,service,dao都可以看成是JavaBean。
实体类的编写规范:
(1)类都是public的
(2)一般实现序列化接口
(3)类成员(字段)都是私有的
(4)私有的类成员都有公有的get/set方法
(5)类都有默认无参构造函数
(6)数据类型的选择:基本数据类型和包装类应该选择包装类型。包装类型的默认值为null,基本数据类型的初始值默认值为0 。
2.Hibernate中的OID(object identifier)
3.Hibernate的主键生产策略
<id name="custId" column="cust_id">
<!-- generator:是指定主键的生成方式。取值是固定的几个之中选一个
native是使用本地数据库的自动增长能力。 -->
<generator class="native"></generator>
</id>
4.Hibernate的一级缓存
什么是缓存?
它就是内存中的临时数据。
为什么使用缓存?
减少和数据库交互的次数,从而提高查询效率。
什么样的数据适用于缓存,什么样的数据不适用缓存?
适用缓存的数据:经常查询的,并且不经常修改的。同时数据一旦出现问题,对最终结果影响不大的。
不适用缓存的数据:不管是否经常查询,只要是经常修改的,都可以不用缓存。
并且如果数据由于使用缓存,产生了异常数据,对最终结果影响很大,则不能使用。
Hibernate的一级缓存:
/**
* 测试Hibernate的一级缓存
*/
@Test
public void testCache(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//1.根据id查询客户
//先去数据库查询记录,并将记录放入一级缓存中
Customer customer1 = session.get(Customer.class, 5);
System.out.println(customer1);
//2.再次根据id查询用户
//先去一级缓存中查询是否有这条记录,如果有,直接拿过来,如果没有再去数据库中查询
Customer customer2 = session.get(Customer.class, 5);//这里hibernate并没有打印sql语句,因为并没有去数据库中查询
System.out.println(customer2);
System.out.println(customer1 == customer2);
tx.commit();
session.close();//session关闭,一级缓存就消失了
}
结果:只有第一次查询的时候向数据库中查询了。
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_address as cust_add6_0_0_, customer0_.cust_phone as cust_pho7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
{'custId':'5','custName':'9','custSource':'null','custIndustry':'null','custLevel':'null','custAddress':'null','custPhone':'null'}
{'custId':'5','custName':'9','custSource':'null','custIndustry':'null','custLevel':'null','custAddress':'null','custPhone':'null'}
true
Session缓存机制:Session内部是一个 map<Class,Map<Object,Object>>
,通过类的字节码获取到该对的缓存map,然后通过OID获取。
5.Hibernate 的快照机制
先看一段代码:
/**
* Hibernate的快照机制
*/
@Test
public void testQuickPhoto(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
//1.根据ID查询客户
//Hibernate在查询到客户后,将该对象保存在一级缓存中,并且同时保存在快照区中
Customer customer1 = session.get(Customer.class, 5);
System.out.println(customer1.getCustAddress());//输出客户的地址:武汉
//2.修改客户的地址 遵义-----此处修改的是一级缓存中的对象
customer1.setCustAddress("遵义");
System.out.println(customer1.getCustAddress());//输出客户的地址:遵义
//没有写update语句
//在提交时,Hibernate会比较一级缓存中的该对象和快照中的是否一致,如果不一致,更新数据库,所以在此处会有一句update打印语句
tx.commit();
session.close();//session关闭
System.out.println(customer1.getCustAddress());//此处输出的是数据库中的值 :遵义
}
Session中有一个一级缓存容器,还有一个快照区 。当执行该语句时session.get(Customer.class, 5);
,Hibernate会把这个对象分别放在一级缓存以及快照(应该是那个对象的副本)中,修改该对象会改变一级缓存中的值,当执行 tx.commit();
时,发现一级缓存中的对象属性值和快照不一致(通过OID比对),Hibernate
会更新数据库中的该条记录。
6.Hibernate中对象的四种状态
(1) 临时状态(瞬时状态):没有OID,和Session无关。刚用 new 关键字创建的对象
(2) 持久状态:有OID,和Session有关。将临时状态进行保存操作、从数据库中查询的记录,对象为持久状态。
(3) 托管状态:有OID,和Session无关。关闭(或者清除clear)session后,持久状态的对象会变为托管状态。
托管状态的对象变为持久状态:更新操作。
(4) 删除状态:有OID,和Session有关。同时已经调用了删除方法 session.delete(obj),但是事务还没提交。 此时对象的状态为删除状态。
/**
* 对象的状态
*/
@Test
public void testStateOfObj(){
Customer c = new Customer();//c:临时状态
c.setCustAddress("贵州");
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(c);//持久状态;或者 :session.saveOrUpdate(c);
tx.commit();
session.close();//session关闭,此时c变为托管状态
//session.clear();//只清除Hibernate中的一级缓存和快照,不关闭session
c.setCustAddress("上海");
Session session1 = HibernateUtil.getSession();
Transaction tx1 = session1.beginTransaction();
session.update(c);//c变为持久状态; 或者:session.saveOrUpdate(c);
tx1.commit();
session1.close();//session关闭,此时c变为托管状态
}
7.Hibernate中的事务控制(配置session与线程绑定)
解决的问题:让session对象也符合使用原则。
session对象的使用原则:一个线程只能有一个Session
-
方法一:使用ThreadLocal保证一个线程只有一个session,但是Hibernate提供配置方法,不用这么做。
HibernateUtil.java
中添加的代码:
private static ThreadLocal<Session> tl = new ThreadLocal();
/**
* 利用ThreadLocal保证一个线程一个session
* @return
*/
public static Session getSession1(){
Session s = tl.get();
if(s == null){
tl.set(sessionFactory.openSession());
}
return tl.get();
}
package com.zwd.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hibernate的工具类
* 用于加载配置、创建一个应用的SessionFactory
*
* @author Administrator
*
*/
public class HibernateUtil {
private static SessionFactory sessionFactory ;
private static ThreadLocal<Session> tl = new ThreadLocal();
//加载配置,并创建sessionFactory
static{
Configuration cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
/**
* 获取一个新的Session(一个线程一个Session)
* @return
*/
public static Session getSession(){
return sessionFactory.openSession();
}
/**
* 利用ThreadLocal保证一个线程一个session
* @return
*/
public static Session getSession1(){
Session s = tl.get();
if(s == null){
tl.set(sessionFactory.openSession());
}
return tl.get();
}
}
-
方法二:在Hibernate的配置文件中配置将session和线程绑定,从而实现一个线程只有一个session。
<!-- 把线程和session绑定,从而实现一个线程只有一个session --> <property name="hibernate.current_session_context_class">thread</property>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <!-- 配置SessionFactory SessionFactory的作用就是用于创建Session对象。 Session对象就是hibernate中操作数据库的核心对象。 此处的操作不要求背,但是要求记住创建SessionFactory必须的三部分信息 第一部分:连接数据库的信息 第二部分:hibernate的可选配置 第三部分:映射文件的位置 --> <session-factory> <!-- 第一部分:连接数据库的信息:可在hibernate包中查找相应的配置 \hibernate-release-5.3.9.Final\project\etc\hibernate.properties --> <!-- ## MySQL #hibernate.dialect org.hibernate.dialect.MySQLDialect #hibernate.dialect org.hibernate.dialect.MySQLInnoDBDialect #hibernate.dialect org.hibernate.dialect.MySQLMyISAMDialect #hibernate.connection.driver_class com.mysql.jdbc.Driver #hibernate.connection.url jdbc:mysql:///test #hibernate.connection.username gavin #hibernate.connection.password --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property><!-- 数据库方言 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/spring</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 第二部分:hibernate的可选配置 --> <property name="hibernate.show_sql">true</property> <!-- 是否显示生产sql语句 --> <property name="hibernate.format_sql">false</property> <!-- 是否使用格式化生产sql语句 --> <!-- 配置hibernate采用何种方式来产生DDL语句 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- update表示检查实体类的映射配置和数据库的表结构是否一致,如果不一致则更改数据库表结构 --> <!-- 把线程和session绑定,从而实现一个线程只有一个session --> <property name="hibernate.current_session_context_class">thread</property> <!-- 配置c3p0连接池 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- SQL结构化查询语言:一共分为6个部分 DDL:Data Definition Language 数据定义语言(建库、建表、创建表结构修改表结构) DML:Data Manipulation Language 数据操纵语言(增、删、改) DQL:Data Query Language 数据查询语言 (查) DCL:Data Control Language 数据控制语言(授权) CCL:Cursor Control Language 游标控制语言 TPL: Transaction Processing Language 事务处理语言 --> <!-- 第三部分:配置映射文件的位置 --> <mapping resource="com/zwd/domain/Customer.hbm.xml"/> </session-factory> </hibernate-configuration>
在获取Session对象的时候也有相应的方法
sessionFactory.getCurrentSession()
,必须在hibernate的配置文件中配置之后才可用这个方法,不然返回值为null。HibernateUtil.java
/**
* 3.在hibernate中配置:使session和线程绑定在一起,从而实现一个线程一个session
* 在线程上获取Session对象。
* @return
*/
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();//必须在hibernate的配置文件中配置之后才可用这个方法,不然返回值为null.
}
package com.zwd.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* Hibernate的工具类
* 用于加载配置、创建一个应用的SessionFactory
*
* @author Administrator
*
*/
public class HibernateUtil {
private static SessionFactory sessionFactory ;
//private static ThreadLocal<Session> tl = new ThreadLocal();
//加载配置,并创建sessionFactory
static{
Configuration cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
/**
* 1.获取一个新的Session(每次获取都会获取到一个新的session)
* @return
*/
public static Session getSession(){
return sessionFactory.openSession();
}
/**
* 2.利用ThreadLocal保证一个线程一个session
* @return
*/
// public static Session getSession1(){
// Session s = tl.get();
// if(s == null){
// tl.set(sessionFactory.openSession());
// }
// return tl.get();
// }
/**
* 3.在hibernate中配置:使session和线程绑定在一起,从而实现一个线程一个session
* 在线程上获取Session对象。
* @return
*/
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();//必须在hibernate的配置文件中配置之后才可用这个方法,不然返回值为null.
}
}
在使用session的时候,不需要我们手动关闭session,
当我们把session和线程绑定之后,hibernate就会在提交或者回滚事务之后,自动帮我们关闭session。
/**
* 使用Hiberbate对session的配置后,
* 测试一个线程只有一个session,
* 并且不需要我们手动关闭session,
* 当我们把session和线程绑定之后,hibernate就会在提交或者回滚事务之后,自动帮我们关闭session
*/
@Test
public void testSession(){
// Session s1 = HibernateUtil.getCurrentSession();
// Session s2 = HibernateUtil.getCurrentSession();
// System.out.println(s1 == s2);
//
Customer c = new Customer();//c:临时状态
c.setCustAddress("深圳1");
Session session = HibernateUtil.getCurrentSession();
System.out.println(session);
Transaction tx = session.beginTransaction();
session.saveOrUpdate(c);//持久状态;或者 :session.saveOrUpdate(c);
tx.commit();
//session.close();// 不需要手动关闭session,事务提交之后session确实不可用了,但是我手动关闭并没有报错
session.get(Customer.class, 5);
System.out.println(session.isOpen());
}
8.Hibernate中的5中查询
-
OID查询:
根据id查询一个实体。
涉及的方法:
get()
和load()
-
SQL查询:
使用SQL语句查询数据库。
涉及的方式有两种:
第一种:
SQLQuery()
------一般不怎么用 第二种:session的
doWork()
方法,它可以拿到Connection -
HQL查询:
使用HQL语句查询数据库。
-
QBC查询:
使用
Criteria
对象查询数据库。 -
对象导航查询:
8.1 Hibernate中的HQL查询–Hibernate Query Language
它是hibernate中的HQL查询方式
HQL:hibernate query language
如何获取该对象:session对象的方法
涉及的对象和方法: createQuery(String hql)
方法中参数的含义:
SQL:select cust_id from cst_customer
HQL:select custId from Customer
HQL语句:是把sql语句的表名换成类名,把字段名换成实体类中的属性名称。
-
基本查询:
//基本查询 @Test public void test1(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //1.获取Query对象 Query query = session.createQuery("from Customer"); //2.获取结果集 List list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
条件查询:
//条件查询 @Test public void test2(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //1.只有问号的占位符已经不能用了,会报错 //Query query = session.createQuery("from Customer where custLevel = ?"); //query.setParameter(1, "23"); //2.占位符方式一: ?数字 Query query = session.createQuery("from Customer where custLevel = ?0"); query.setParameter(0, "23"); //3.占位符方式二: :名称 Query query = session.createQuery("from Customer where custLevel = :custLevel and custAddress like :custAddress"); query.setParameter("custLevel", "23"); query.setParameter("custAddress", "%深圳%"); List list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
1.只有问号的占位符已经不能用了,会报错
Caused by: org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported; use JPA-style ordinal parameters (e.g., `?1`) instead : from com.zwd.domain.Customer where custLevel = ?
- 排序查询:
/**排序查询:
* 降序:order by desc;
* 升序:order by asc
*/
@Test
public void test3(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Customer order by custId desc");
List list = query.list();
for (Object object : list) {
System.out.println(object);
}
tx.commit();
}
-
分页查询:
/**分页查询 * * MySql分页关键字: * limit * limit的两个参数含义: * 第一个:查询的开始记录索引(等于 (当前页-1)*每页条数) * 第二个:每次查询的条数 * Hibernate为我们提供了两个方法:Mysql和Oracle都可用这个(设置不同的方言即可) * setFirstResult:设置查询的开始记录索引 * setMaxResults:设置每次查询的条数 */ @Test public void test4(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //1.获取Query对象 Query query = session.createQuery("from Customer"); query.setFirstResult(0);//从0开始 query.setMaxResults(2); //2.获取结果集 List list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
统计查询:
query.uniqueResult();//返回一个唯一的结果,如果结果不唯一则会抛异
/**统计查询
* 聚合函数:count、sum、max、min、avg
*
*/
@Test
public void test5(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//1.获取Query对象
Query query = session.createQuery("select count(*) from Customer");
//2.获取结果集
long count = (long) query.uniqueResult();//返回一个唯一的结果,如果结果不唯一则会抛异常。
tx.commit();
}
-
投影查询: 当我们在查询实体时,只需要部分字段,而不是全部。并且希望它的返回结果使用实体类来封装,而不是Object[],这个时候我们称之为创建实体类的投影。
当只查询部分字段时返回的是数组。
@Test public void test6(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //1.获取Query对象 Query query = session.createQuery("select custId,custName from Customer");//返回结果的每条记录将会在一个数组中 //2.获取结果集 List<Object[]> list = query.list(); for (Object[] os : list) { System.out.println("======每个数组中的内容======="); for (Object o : os) { System.out.println(o); } } tx.commit(); }
使用投影:将返回的结果封装成实体类。
投影查询的用法:
-
1、查询语句时需要使用new关键字。
session.createQuery("select new com.zwd.domain.Customer(custId,custName) from Customer");
-
2、在实体类中添加对应参数的构造函数。添加之后注意实体类的编写规范
//当添加了带参数的构造函数后,需要自己再添加一个无参构造函数 public Customer() { } public Customer(int custId, String custName) { this.custId = custId; this.custName = custName; }
-
/**投影查询
* 当我们在查询实体时,只需要部分字段,而不是全部。并且希望它的返回结果使用实体类来封装,而不是Object[]
*这个时候我们称之为创建实体类的投影。
*
* 投影查询的用法:
* 1、查询语句时需要使用new关键字。
* 2、在实体类中添加对应参数的构造函数。
*
*/
@Test
public void test7(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//1.获取Query对象
Query query = session.createQuery("select new com.zwd.domain.Customer(custId,custName) from Customer");
//2.获取结果集
List list = query.list();
for (Object object : list) {
System.out.println(object);
}
tx.commit();
}
8.2 Hibernate中的QBC查询–Query By Criteria
Hibernate中的Criteria对象:
它是hibernate中QBC查询的方式。
QBC: Query By Criteria
如何获取该对象:
session.createCriteria(Class clazz);
涉及对象的方法:
createCriteria(Class clazz);
参数的含义:要查询的实体类字节码。
-
基本查询:
// 基本查询 @Test public void test1() { Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); // 获取Criteria对象 Criteria criteria = session.createCriteria(Customer.class); // 获取结果集 List list = criteria.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
条件查询: 添加条件:
criteria.add(Restrictions.eq("custLevel", "23"));
// 条件查询 @Test public void test2() { Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); // 获取Criteria对象 Criteria criteria = session.createCriteria(Customer.class); criteria.add(Restrictions.eq("custLevel", "23")); criteria.add(Restrictions.like("custAddress", "%深圳%")); // 获取结果集 List list = criteria.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
排序查询: 设置排序:
criteria.addOrder(Order.desc("custId"));
// 排序查询 @Test public void test3() { Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); // 获取Criteria对象 Criteria criteria = session.createCriteria(Customer.class); criteria.addOrder(Order.desc("custId")); // 获取结果集 List list = criteria.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
分页查询: 和Query对象的一样的方法。
criteria.setFirstResult(0);criteria.setMaxResults(2);
// 分页查询 @Test public void test4() { Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); // 获取Criteria对象 Criteria criteria = session.createCriteria(Customer.class); criteria.setFirstResult(0); criteria.setMaxResults(2); // 获取结果集 List list = criteria.list(); for (Object object : list) { System.out.println(object); } tx.commit(); }
-
统计查询: 使用聚合函数:
criteria.setProjection(Projections.count("custName"));
获取单一结果:
criteria.uniqueResult();
//统计查询 @Test public void test5() { Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); // 获取Criteria对象 Criteria criteria = session.createCriteria(Customer.class); //criteria.setProjection(Projections.rowCount());//count(*) criteria.setProjection(Projections.count("custName")); // 获取结果集 long count = (long) criteria.uniqueResult(); System.out.println(count); tx.commit(); }
-
离线查询:
DetachedCriteria对象,该对象的获取不需要session,可以直接得到,我们使用此对象实现的查询,称之为离线查询。 Criteria对象的获取需要session。在线对象。
//1.获取离线对象 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.eq("custLevel", "23")); dc.add(Restrictions.like("custAddress", "%深圳%")); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //2.把离线对象转为在线对象 Criteria criteria = dc.getExecutableCriteria(session); //3.获取结果集 List list = criteria.list(); tx.commit();
/** 离线查询 * DetachedCriteria对象,该对象的获取不需要session,可以直接得到,我们使用此对象实现的查询,称之为离线查询。 * Criteria对象的获取需要session。在线对象。 */ @Test public void testServlet(){ //获取离线对象 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.eq("custLevel", "23")); dc.add(Restrictions.like("custAddress", "%深圳%")); List list = testService(dc); for(Object o : list){ System.out.println(o); } } private List testService(DetachedCriteria dc) { Session session = null; Transaction tx = null; try{ session = HibernateUtil.getCurrentSession(); tx = session.beginTransaction(); List list = testDao(dc); tx.commit(); return list; }catch(Exception e){ System.out.println(e.getMessage()); tx.rollback(); }finally{ //hibernate已经帮我们关闭了session } return null; } private List testDao(DetachedCriteria dc) { Session session = HibernateUtil.getCurrentSession(); //把离线对象转为在线对象 Criteria criteria = dc.getExecutableCriteria(session); return criteria.list(); }
8.3 第二天总结
-
数据表:
cst_customer
DROP TABLE IF EXISTS `cst_customer`; CREATE TABLE `cst_customer` ( `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)', `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)', `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源', `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业', `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别', `cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址', `cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
-
首先创建工程,导Hibernate必须的包 + 数据库连接驱动 + 连接池
-
创建包
com.zwd.domain
,创建数据库表对应的实体类Customer
。注意实体类的编写规范。 -
在实体类的包下创建实体类的映射文件:在映射id时,记得指定主键生成的方式 。 导入xml文件的dtd约束,约束在hibernate核心包中查找。具体如 Hibernate第一天(1)。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="Customer" table="cst_customer"> <id name="custId" column="cust_id"> <generator class="native"></generator> <!-- 指定主键生成方式 --> </id> <property name="custName" column="cust_name"></property> <property name="custSource" column="cust_source"></property> <property name="custIndustry" column="cust_industry"></property> <property name="custLevel" column="cust_level"></property> <property name="custAddress" column="cust_address"></property> <property name="custPhone" column="cust_phone"></property> </class> </hibernate-mapping>
-
创建Hibernate的总配置文件:
Hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 1.配置连接的数据库信息 --> <!-- 数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 连接数据的信息 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/spring</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 2.Hibernate的可选配置 --> <!-- 配置数据库连接池 c3p0 :记得导c3p0的包(在hibernate包的option中)--> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- 配置session与线程绑定 --> <property name="hibernate.current_session_context_class">thread</property> <!-- 是否生成sql语句打印 --> <property name="hibernate.show_sql">true</property> <!-- 是否格式化生成sql语句 --> <property name="hibernate.format_sql">true</property> <!-- 采用怎样的方式处理ddl --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 3.配置映射文件的位置 --> <mapping resource="com/zwd/domain/Customer.xml" /> </session-factory> </hibernate-configuration>
-
hibernate的基本操作过程
package com.zwd.test; import java.util.List; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.query.Query; import org.junit.Test; /** * hibernate的基本操作过程 * 问题:应用只加载一次配置文件,一个SessionFactory对象即可 * @author Administrator * */ public class HibernateDemoBase { @Test public void test(){ //1.读取hibernate的总配置文件 Configuration cfg = new Configuration(); cfg.configure();//去找类路径下的:Hibernate.cfg.xml,如果没有则会报错 //cfg.configure("Hibernate1.cfg.xml");//在类路径中去加载指定文件 //2.根据配置获取sessionFactory SessionFactory sessionFactory = cfg.buildSessionFactory(); //3.根据sessionFactory获取session //sessionFactory.openSession(); Session session = sessionFactory.getCurrentSession();//需配置xml才能使用,保证session的使用原则,一个线程一个session //4.根据session获取并开启事务 Transaction tx = session.beginTransaction(); //5.操作数据库:两种方式:HQL,使用 Query对象操作。QBC:使用Criteria操作 Query query = session.createQuery("from Customer"); List list = query.list(); for (Object object : list) { System.out.println(object); } tx.commit(); //tx.rollback(); } }
-
将加载配置文件的代码抽取到
HibernateUtil.java
中。package com.zwd.utils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; /** * Hibernate的工具类: * 用于加载配置文件,以及获取session对象 * @author Administrator * */ public class HibernateUtil { private static SessionFactory sessionFactory = null; //加载配置文件 static{ Configuration cfg = new Configuration(); cfg.configure();//加载配置文件 sessionFactory = cfg.buildSessionFactory(); } public static Session getSeesion(){ return sessionFactory.openSession(); } //获取一个线程的session对象,需配置xml文件,该session不需要手动close public static Session getCurrentSession(){ return sessionFactory.getCurrentSession(); } }
-
hibernate 中的增、删、改、查一个
保存: session.save(obj); 查询一个: session.get(class,primaryKey)、session.load(class,primaryKey) 更新: session.update(obj) 删除: session.delete(obj)
-
hibernate 的HQL查询(一条或多条):使用得较多,相对QBC要简单一点。
Query query = session.createQuery(String hql) List list = query.list();//返回多条 query.uniqueResult();//返回单个值
-
hibernate 的QBC查询(一条或多条):
Criteria criteria = session.createCriteria(Class clazz); //设置条件 criteria.add(Restrictions.eq("custLevel", "23")); //设置排序 criteria.order(Order.asc("custId")) //设置分页 criteria.setFirstResult(0); criteria.setMaxResults(2); //设置统计 criteria.setProjection(Projections.count("custName")); //设置离线查询 //1.获取离线对象 DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.eq("custLevel", "23")); dc.add(Restrictions.like("custAddress", "%深圳%")); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //2.把离线对象转为在线对象 Criteria criteria = dc.getExecutableCriteria(session);
Hibernate第三天
1.表之间的关系
-
数据库中表关系: 一对一、一对多(多对一)、多对多。
-
如何确立和实现数据库中的表关系
一对多的表关系在数据库中如何实现?
使用外键约束。
我们习惯把一的方称为主表,把多的方称为从表。
什么是外键:
从表中有一列,该列的取值除了null之外,只能来源于主表的主键。
默认情况下,外键字段的值是可以重复的。
多对多的表关系在数据库中如何实现?
使用中间表。
中间表中只有两个外键,引用两个多对多的主键。
不能有其他字段信息,至于中间表的主键,应该采用联合主键。
任何一个对方的表和中间表去比较,都是一对多的关系。
一对一的表关系在数据库如何实现?(两种)
第一种:建立外键的方式。
使用外键约束,唯一约束,非空约束。
它是把外键字段加了非空和唯一约束。从而实现了一对一。
第二种:使用主键的方式。
让其中一张表既是主键,又是外键。
如何确立两张表之间的关系:
找外键。
2.学习多表映射配置要遵循的步骤
第一步:确立两张表之间的关系。
第二步:在数据库中实现两张表之间的关系
第三步:在实体类中描述出两个实体之间的关系
第四步:在映射配置文件中建立两个实体和两张表之间的关系
3.一对多的关系映射及操作
示例:客户和联系人表之间的关系。
第一步:确立两张表之间的关系。
一个客户可以包含多个联系人。
多个联系人可以属于同一个客户。
所以说:客户和联系人之间的关系是一对多。
第二步:在数据库中实现两张表之间的关系建立。
实现一对多的关系,靠外键。
客户表是主表,联系人表是从表。
我们需要在联系人表中添加外键。
第三步:在实体类中描述出两个实体之间的关系
主表的实体类应该包含从表实体类的集合引用。
从表的实体类应该包含主表实体类的对象引用。
第四步:在映射配置文件中建立两个实体和两张表之间的关系
-
在实体类中描述出两个实体之间的关系。
主表的实体类应该包含从表实体类的集合引用。
//一对多关系映射:一的一方 //主表的实体类应该包含从表实体类的集合引用。 Set<LinkMan> set = new HashSet<LinkMan>(0);
package com.zwd.domain; import java.util.HashSet; import java.util.Set; /******************************************************************************* * javaBeans * cst_customer --> Customer * <table explanation> * @author 2019-08-12 14:43:39 * */ public class Customer implements java.io.Serializable { //field /** **/ private int custId; /** **/ private String custName; /** **/ private String custSource; /** **/ private String custIndustry; /** **/ private String custLevel; /** **/ private String custAddress; /** **/ private String custPhone; /** **/ private String custTest; //一对多关系映射:一的一方 //主表的实体类应该包含从表实体类的集合引用。 Set<LinkMan> set = new HashSet<LinkMan>(0); public Set<LinkMan> getSet() { return set; } public void setSet(Set<LinkMan> set) { this.set = set; } //method public int getCustId() { return custId; } public void setCustId(int custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } public String getCustTest() { return custTest; } public void setCustTest(String custTest) { this.custTest = custTest; } //override toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'custId':'"+this.getCustId()+"',"); sb.append("'custName':'"+this.getCustName()+"',"); sb.append("'custSource':'"+this.getCustSource()+"',"); sb.append("'custIndustry':'"+this.getCustIndustry()+"',"); sb.append("'custLevel':'"+this.getCustLevel()+"',"); sb.append("'custAddress':'"+this.getCustAddress()+"',"); sb.append("'custPhone':'"+this.getCustPhone()+"',"); sb.append("'custTest':'"+this.getCustTest()+"'"); sb.append("}"); return sb.toString(); } }
从表的实体类应该包含主表实体类的对象引用。
//一对多关系映射:多的一方 //从表的实体类应该包含主表实体类的对象引用。 private Customer customer;
package com.zwd.domain; /******************************************************************************* * javaBeans * cst_linkman --> CstLinkman * <table explanation> * @author 2019-08-12 21:30:10 * */ public class LinkMan implements java.io.Serializable { //field /** 联系人编号(主键) **/ private long lkmId; /** 联系人姓名 **/ private String lkmName; /** 联系人性别 **/ private String lkmGender; /** 联系人办公电话 **/ private String lkmPhone; /** 联系人手机 **/ private String lkmMobile; /** 联系人邮箱 **/ private String lkmEmail; /** 联系人职位 **/ private String lkmPosition; /** 联系人备注 **/ private String lkmMemo; //一对多关系映射:多的一方 //从表的实体类应该包含主表实体类的对象引用。 private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } //method public long getLkmId() { return lkmId; } public void setLkmId(long lkmId) { this.lkmId = lkmId; } public String getLkmName() { return lkmName; } public void setLkmName(String lkmName) { this.lkmName = lkmName; } public String getLkmGender() { return lkmGender; } public void setLkmGender(String lkmGender) { this.lkmGender = lkmGender; } public String getLkmPhone() { return lkmPhone; } public void setLkmPhone(String lkmPhone) { this.lkmPhone = lkmPhone; } public String getLkmMobile() { return lkmMobile; } public void setLkmMobile(String lkmMobile) { this.lkmMobile = lkmMobile; } public String getLkmEmail() { return lkmEmail; } public void setLkmEmail(String lkmEmail) { this.lkmEmail = lkmEmail; } public String getLkmPosition() { return lkmPosition; } public void setLkmPosition(String lkmPosition) { this.lkmPosition = lkmPosition; } public String getLkmMemo() { return lkmMemo; } public void setLkmMemo(String lkmMemo) { this.lkmMemo = lkmMemo; } //override toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'1kmId':'"+this.getLkmId()+"',"); sb.append("'lkmName':'"+this.getLkmName()+"',"); sb.append("'lkmGender':'"+this.getLkmGender()+"',"); sb.append("'1kmPhone':'"+this.getLkmPhone()+"',"); sb.append("'lkmMobile':'"+this.getLkmMobile()+"',"); sb.append("'lkmEmail':'"+this.getLkmEmail()+"',"); sb.append("'lkmPosition':'"+this.getLkmPosition()+"',"); sb.append("'lkmMemo':'"+this.getLkmMemo()+"'"); sb.append("}"); return sb.toString(); } }
-
在映射配置文件中建立两个实体和两张表之间的关系
在从表的配置文件中配置映射关系。
<!-- 多对一的关系映射: 涉及的标签:<many-to-one/> 标签的属性: name:实体类中主表实体类的对象引用的名称(成员变量中主表实体类的对象名称) class:主表类类名 column:指定该表中的外键名称--> <!--单向多对一:查询id的为1的联系人属性哪个客户 select * cst_customer where cust_id = (select lkmId from cst_linkman where lkmId = 1); --> <many-to-one name="customer" class = "Customer" column="lkm_cust_id"></many-to-one>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="LinkMan" table="cst_linkman"> <id name="lkmId" column="lkmId"> <generator class="native"></generator> <!-- 指定主键生成方式 --> </id> <property name="lkmName" column="lkm_name"></property> <property name="lkmGender" column="lkm_gender"></property> <property name="lkmPhone" column="lkm_phone"></property> <property name="lkmMobile" column="lkm_mobile"></property> <property name="lkmEmail" column="lkm_email"></property> <property name="lkmPosition" column="lkm_position"></property> <property name="lkmMemo" column="lkm_memo"></property> <!-- 多对一的关系映射: 涉及的标签:<many-to-one/> 标签的属性: name:实体类中主表实体类的对象引用的名称(成员变量中主表实体类的对象名称) class:主表类类名 column:指定该表中的外键名称--> <!--单向多对一:查询id的为1的联系人属性哪个客户 select * cst_customer where cust_id = (select lkmId from cst_linkman where lkmId = 1); --> <many-to-one name="customer" class = "Customer" column="lkm_cust_id"></many-to-one> </class> </hibernate-mapping>
在主表映射的文件中添加配置
<!-- 一对多的关系映射:主表实体的映射配置 涉及的标签: set: 作用:用于配置set集合属性 属性: name:指定实体类中set集合的属性名称 table:指定从表的名称。在一对多配置时可以不写 key: 作用:用于映射外键字段 属性: column:指定外键字段名称 one-to-many: 作用:用于建立一对多的映射配置 属性: class:用于指定从表实体的名称。--> <!-- 单向一对多:查询id为1的客户对应哪些联系人 select * from cst_linkman where lkm_cust_id = 1 --> <set name="linkmans" table="cst_linkman"> <key column="lkm_cust_id"></key> <one-to-many class="LinkMan"/> </set>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="Customer" table="cst_customer"> <id name="custId" column="cust_id"> <generator class="native"></generator> <!-- 指定主键生成方式 --> </id> <property name="custName" column="cust_name"></property> <property name="custSource" column="cust_source"></property> <property name="custIndustry" column="cust_industry"></property> <property name="custLevel" column="cust_level"></property> <property name="custAddress" column="cust_address"></property> <property name="custPhone" column="cust_phone"></property> <!-- 一对多的关系映射:主表实体的映射配置 涉及的标签: set: 作用:用于配置set集合属性 属性: name:指定实体类中set集合的属性名称 table:指定从表的名称。在一对多配置时可以不写 key: 作用:用于映射外键字段 属性: column:指定外键字段名称 one-to-many: 作用:用于建立一对多的映射配置 属性: class:用于指定从表实体的名称。--> <set name="linkmans" table="cst_linkman"> <key column="lkm_cust_id"></key> <one-to-many class="LinkMan"/> </set> </class> </hibernate-mapping>
4.一对多的CRUD操作
保存:
- 1、保存一个联系人,属于一个已存在的客户
/**
* 1、 保存一个联系人,属于一个已存在的客户
* 正常保存:创建一个新的联系人,需要关联一个客户
*/
@Test
public void test1(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//1.查询一个客户
Customer customer = session.get(Customer.class, 95);
//2.创建一个新的联系人
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("一对多的联系人1");
//3.关联一个客户
linkMan.setCustomer(customer);
//4.保存一个联系人
session.save(linkMan);
tx.commit();
}
-
2、保存一个客户和一个联系人
set标签的inverse属性:是否放弃维护关联关系的权利。ture:放弃;false:不放弃(默认值)
/**
* 2、保存一个客户和一个联系人
* 1.创建一个客户和一个联系人
* 2.建立联系人和客户的双向关联关系
* 3.使用符合原则的保存:先保存主表实体,再保存从表实体。
* 上诉步骤还存在一个问题:我们保存两个实体,应该只有两条insert语句,
* 而执行结果却多了一条update语句。因为在保存客户时,会保存
* 该客户所对应的联系人的信息,但此时联系人的id为空,当保存联系人后,
* 联系人的id不为空,在commit的时候两个快照信息不一致,所以会有更新id语句
* 会更新id是因为Customer.xml中的配置。
* 解决办法:在Customer.xml中配置
* 在set标签中添加inverse属性
* inverse:是否放弃维护关联关系的权利
* ture:放弃
* false:不放弃(默认值)
*
*/
@Test
public void test2(){
//1.创建一个客户和一个联系人
Customer customer = new Customer();
customer.setCustName("一对多的客户2");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("一对多的联系人4");
//2.建立联系人和客户的双向关联关系
customer.getLinkmans().add(linkMan);
linkMan.setCustomer(customer);
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//3.先保存主表实体,再保存从表实体。
session.save(customer);
session.save(linkMan);
tx.commit();
}
<set name="linkmans" table="cst_linkman" inverse="true">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
-
3、级联保存:在实体类的配置中,在关系配置的地方使用
cascade
属性,级联保存更新的取值:save-update
。cascade="save-update"
1、级联保存–保存客户时级联保存该客户所对应的联系人
/** * 级联保存--保存客户时级联保存该客户所对应的联系人 * 首先需要在客户的配置文件中配置: * 在set标签中使用cascade属性 * cascade:配置级联操作 * 级联保存更新的取值:save-update * 1、创建新的客户和新的联系人 * 2、建立客户和联系人之间的双向关系 * 3、保存客户并级联保存联系人 */ @Test public void test3(){ //1、创建新客户和新的联系人 Customer customer = new Customer(); LinkMan linkMan = new LinkMan(); customer.setCustName("一对多级联保存1"); linkMan.setLkmName("一对多级联保存1"); //2、建立客户和联系人之间的双向关系 customer.getLinkmans().add(linkMan); linkMan.setCustomer(customer); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //2、在保存客户时级联保存联系人 session.save(customer); tx.commit(); }
在客户实体类的配置文件中添加对级联的支持。
<set name="linkmans" table="cst_linkman" inverse="true" cascade="save-update"> <key column="lkm_cust_id"></key> <one-to-many class="LinkMan"/> </set>
2、级联保存–保存联系人时级联保存该联系人所对应的客户
/** * 级联保存--保存联系人时级联保存该联系人所对应的客户 * 在联系人中使用cascade属性 * 1、创建新的客户和新的联系人 * 2、建立客户和联系人之间的双向关系 * 3、保存客户并级联保存联系人 */ @Test public void test4(){ //1、创建新客户和新的联系人 Customer customer = new Customer(); LinkMan linkMan = new LinkMan(); customer.setCustName("一对多级联保存2"); linkMan.setLkmName("一对多级联保存2"); //2、建立客户和联系人之间的双向关系 customer.getLinkmans().add(linkMan); linkMan.setCustomer(customer); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); //2、在保存客户时级联保存联系人 session.save(linkMan); tx.commit(); }
在联系人实体类的配置文件中的配置。
<many-to-one name="customer" class = "Customer" column="lkm_cust_id" cascade="save-update"></many-to-one>
更新:级联更新:
还是使用属性 cascade="save-update"
:对一个对象的保存或更新操作会涉及它的关联对象的更新或保存。
Customer.xml中的配置。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.zwd.domain">
<class name="Customer" table="cst_customer">
<id name="custId" column="cust_id">
<generator class="native"></generator> <!-- 指定主键生成方式 -->
</id>
<property name="custName" column="cust_name"></property>
<property name="custSource" column="cust_source"></property>
<property name="custIndustry" column="cust_industry"></property>
<property name="custLevel" column="cust_level"></property>
<property name="custAddress" column="cust_address"></property>
<property name="custPhone" column="cust_phone"></property>
<!-- 一对多的关系映射:主表实体的映射配置
涉及的标签:
set:
作用:用于配置set集合属性
属性:
name:指定实体类中set集合的属性名称
table:指定从表的名称。在一对多配置时可以不写
key:
作用:用于映射外键字段
属性:
column:指定外键字段名称
one-to-many:
作用:用于建立一对多的映射配置
属性:
class:用于指定从表实体的名称。-->
<!-- 单向一对多:查询id为1的客户对应哪些联系人
select * from cst_linkman where lkm_cust_id = 1 -->
<set name="linkmans" table="cst_linkman" inverse="true" cascade="save-update">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
</class>
</hibernate-mapping>
/**
* 级联更新:
* 创建一个新的联系人,查询一个客户
* 建立联系人和客户之间的双向关系
* 更新客户
*/
@Test
public void test5(){
//1、创建一个新的联系人
LinkMan linkMan = new LinkMan();
linkMan .setLkmName("级联更新3");
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//2、获取一个已存在的客户
Customer customer = session.get(Customer.class, 95);
//3、建立联系人与客户之间的双向关系
customer.getLinkmans().add(linkMan);
linkMan.setCustomer(customer);
//4、更新客户
session.update(customer);
tx.commit();
}
删除:级联删除:
删除“多”的方相当于单表的删除。
删除“一”的方需要级联删除。
需要在"一"的方中的配置文件配置级联删除。在Customer.xml中的配置,多个级联操作以逗号隔开。
<set name="linkmans" table="cst_linkman" cascade="save-update,delete">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
1、如果外键可以为null并且将关系的维护交给"一"的一方(在set中没有配置inverse=true
),hibernate在删除"一"的方的数据时,会先更新该"一"所对应的"多"的外键值为null后,删除"多"方,再删除“一”方。
/**
* 级联删除1:
* 如果外键可以为null并且将关系的维护交给"一"的一方(在set中没有配置`inverse=true`),如果外键不允许为null并且也没有放弃维护关系的权利,则会报错,因为不能将外键值置为null。
* hibernate在删除"一"的方的数据时,会先更新该"一"所对应的"多"的外键值为null,然后再删除"一"方。
*/
@Test
public void test6(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 96);
session.delete(customer);
tx.commit();
}
执行结果:
Hibernate: update cst_linkman set lkm_cust_id=null where lkm_cust_id=?
Hibernate: delete from cst_linkman where lkmId=?
Hibernate: delete from cst_customer where cust_id=?
2、将维护关系的权利交给“多”的一方,Customer放弃维护关系的权利,在Customer.xml中配置 inverse = true
<set name="linkmans" table="cst_linkman" inverse="true" cascade="save-update,delete">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
/**
* 级联删除2:
* 将关系的维护交给“多”方,在Customer.xml中配置 inverse = true
* 在删除主表记录时先删除字表的关联记录,然后再删除主表记录。
*
*/
@Test
public void test7(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
Customer customer = session.get(Customer.class, 103);
session.delete(customer);
tx.commit();
}
执行结果:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_address as cust_add6_0_0_, customer0_.cust_phone as cust_pho7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
Hibernate: select linkmans0_.lkm_cust_id as lkm_cust9_1_0_, linkmans0_.lkmId as lkmId1_1_0_, linkmans0_.lkmId as lkmId1_1_1_, linkmans0_.lkm_name as lkm_name2_1_1_, linkmans0_.lkm_gender as lkm_gend3_1_1_, linkmans0_.lkm_phone as lkm_phon4_1_1_, linkmans0_.lkm_mobile as lkm_mobi5_1_1_, linkmans0_.lkm_email as lkm_emai6_1_1_, linkmans0_.lkm_position as lkm_posi7_1_1_, linkmans0_.lkm_memo as lkm_memo8_1_1_, linkmans0_.lkm_cust_id as lkm_cust9_1_1_ from cst_linkman linkmans0_ where linkmans0_.lkm_cust_id=?
Hibernate: delete from cst_linkman where lkmId=?
Hibernate: delete from cst_customer where cust_id=?
查询:对象导航查询
hibernate的查询:OID查询、HQL查询、QBC查询、SQL查询、对象导航查询。
/**
* 对象的导航查询:
* 查询操作:OID查询、HQL查询、QBC查询、SQL查询。
* hibernate中的最后一种查询操作:对象导航查询。
* 当我们通过调用get×××方法即可实现查询功能
*/
@Test
public void test8(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//根据id获取一个customer对象
Customer customer = session.get(Customer.class, 104);
System.out.println(customer);
//获取该用户下的所有联系人,第一次用到的时候才会查询保存在集合中
Set<LinkMan> linkmans = customer.getLinkmans();//在此处不会去查询
System.out.println(linkmans);//此处才会去数据库中查询
tx.commit();
}
/**
* 获取当前联系人所对应的客户
*/
@Test
public void test9(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//根据id获取联系人
LinkMan linkMan = session.get(LinkMan.class,17L);
System.out.println(linkMan);
//查询当前联系人所对应的客户
System.out.println(linkMan.getCustomer());
tx.commit();
}
lazy属性:
(1)class的lazy属性:它只管load方法是否是延迟加载。属性值:true(默认值)、false
(2)set标签以及many-to-one的lazy标签:它管的是关联对象是否是延迟加载,如果关联对象是延迟加载,则关联对象只有在第一次使用时才会使用。如果关联对象是立即加载,则当前对象加载时就会立即加载关联对象。
set标签中lazy的属性值:true(默认值)、false。
many-to-one标签中lazy的属性值:false、proxy(默认值)、no-proxy
set标签的lazy属性:一对多时,根据一的一方查询多的一方时,需要使用延迟加载。(默认配置即可)
<set name="linkmans" table="cst_linkman" inverse="true" cascade="save-update,delete" lazy="false">
<key column="lkm_cust_id"></key>
<one-to-many class="LinkMan"/>
</set>
/**
* 查询id为105的客户下所有的联系人 customer.getLinkmans();
* 一对多时,根据一的一方查询多的一方时,需要使用延迟加载。(默认配置即可)
* 延迟加载:
* customer = session.get(Customer.class, 104);//此处只查询客户
* Set<LinkMan> linkmans = customer.getLinkmans();//在此处不会去查询联系人
* System.out.println(linkmans);//此处才会去数据库中查询联系人
*
* 如果需要改为立即加载:在customer.xml的set标签中添加 lazy=false
* set标签中的lazy属性的取值:
* true:延迟加载(默认取值);
* false:立即加载
* 立即加载:
* customer = session.get(Customer.class, 104);//此处会立即查询客户以及客户的所有联系人,之后不再查询
*/
@Test
public void test8(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//根据id获取一个customer对象
Customer customer = session.get(Customer.class, 104);//延迟加载时:此处只查询客户
System.out.println(customer);
//获取该用户下的所有联系人,延迟加载时:第一次用到的时候才会查询保存在集合中
Set<LinkMan> linkmans = customer.getLinkmans();//延迟加载时:在此处不会去查询联系人
System.out.println(linkmans);//延迟加载时:此处才会去数据库中查询联系人
tx.commit();
}
many-to-one标签的lazy属性:多对一时,根据多的一方查询一的一方时,不需要使用延迟加载,而是立即加载,需要配置一下
<many-to-one name="customer" class = "Customer" lazy="false"
column="lkm_cust_id" cascade="save-update">
</many-to-one>
/**
* 查询id为17的联系人属于哪个客户
* 多对一时,根据多的一方查询一的一方时,不需要使用延迟加载,而是立即加载,需要配置一下
* many-to-one标签中的lazy属性的取值:
* false:立即加载;
* proxy(默认值):看load方法是延迟加载还是立即加载,load方法默认是延迟加载。将load方法改 为立即加载如下。
* no-proxy:不管。
*/
@Test
public void test9(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//根据id获取联系人
LinkMan linkMan = session.get(LinkMan.class,17L);
System.out.println(linkMan);
//查询当前联系人所对应的客户
Customer customer = linkMan.getCustomer();//在此处不会查询
System.out.println(customer);//第一次使用时才查询(延迟加载)
tx.commit();
}
class标签的lazy属性:决定的是load方法是否是延迟加载
<class name="LinkMan" table="cst_linkman" lazy="false"> ....... </class>
/**
* 将load方法改为立即加载
* 在实体类的配置文件的class标签中添加 lazy属性。
* 属性值:true:延迟加载
* false:立即加载
*
* class标签的lazy:它只能管load方法是否是延迟加载。
* set标签的lazy:它只管查询关联的集合对象是否是延迟加载。
* many-to-one的lazy:它管的是查询关联的主表实体是否立即加载。
*/
@Test
public void test10(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
//根据id获取联系人
LinkMan linkMan = session.load(LinkMan.class,17L);
System.out.println(linkMan);
Customer customer = linkMan.getCustomer();
System.out.println(customer);
tx.commit();
}
5.多对多的映射操作
示例:用户和角色。
第一步:确立两张表之间的关系。
一个用户对应多种角色。
一个角色对应多个用户。
所以角色和用户之间是多对多。
第二步:在数据库中实现两张表之间的关系
在数据库中实现多对多要靠中间表。
中间表中只能出现用户和角色主键。
第三步:在实体类中描述出两个实体之间的关系
第四步:在映射配置文件中建立两个实体和两张表之间的关系
-
在实体类中描述出两个实体之间的关系
用户表:SysUser.java
//多对多关系映射:一个用户对应多个角色 Set<SysRole> roles = new HashSet<SysRole>(0);
package com.zwd.domain; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class SysUser implements Serializable{ private long userId; private String userName; private String userPassWord; //多对多关系映射:一个用户对应多个角色 Set<SysRole> roles = new HashSet<SysRole>(0); public Set<SysRole> getRoles() { return roles; } public void setRoles(Set<SysRole> roles) { this.roles = roles; } public long getUserId() { return userId; } public void setUserId(long userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassWord() { return userPassWord; } public void setUserPassWord(String userPassWord) { this.userPassWord = userPassWord; } @Override public String toString() { return "SysUser [userId=" + userId + ", userName=" + userName + ", userPassWord=" + userPassWord + "]"; } }
角色表:SysRole.java
//多对多关系映射:一个角色对应多个用户 Set<SysUser> users = new HashSet<SysUser>(0);
package com.zwd.domain; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class SysRole implements Serializable{ private long roleId; private String roleName; private String roleMemo; //多对多关系映射:一个角色对应多个用户 Set<SysUser> users = new HashSet<SysUser>(0); public Set<SysUser> getUsers() { return users; } public void setUsers(Set<SysUser> users) { this.users = users; } public long getRoleId() { return roleId; } public void setRoleId(long roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleMemo() { return roleMemo; } public void setRoleMemo(String roleMemo) { this.roleMemo = roleMemo; } @Override public String toString() { return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]"; } }
-
在映射配置文件中建立两个实体和两张表之间的关系
多对多的关系映射: 涉及的标签: set: 作用:用于映射set集合属性 属性: name:指定集合的名称 table:指定的是中间表的名称 key: 作用:用于映射外键字段。 属性: column:指定的是当前实体在中间表的外键字段名称。 many-to-many: 作用:用于映射多对多的关系。 属性: class:对方的实体类名称 column:对方在中间表的外键字段名称
SysUser.hbm.xml
<set name="roles" table="user_role"> <key column="user_id"></key> <many-to-many class="SysRole" column="role_id"></many-to-many> </set>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="SysUser" table="sys_user"> <id name="userId" column="user_id"> <generator class="identity"></generator> </id> <property name="userName" column="user_name"></property> <property name="userPassWord" column="user_password"></property> <set name="roles" table="user_role"> <key column="user_id"></key> <many-to-many class="SysRole" column="role_id"></many-to-many> </set> </class> </hibernate-mapping>
SysRole.hbm.xml
<set name="users" table="user_role" > <key column="role_id"></key> <many-to-many class="SysUser" column="user_id"></many-to-many> </set>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zwd.domain"> <class name="SysRole" table="sys_role"> <id name="roleId" column="role_id"> <generator class="identity"></generator> </id> <property name="roleName" column="role_name"></property> <property name="roleMemo" column="role_password"></property> <!-- 多对多的关系映射: 涉及的标签: set: 作用:用于映射set集合属性 属性: name:指定集合的名称 table:指定的是中间表的名称 key: 作用:用于映射外键字段。 属性: column:指定的是当前实体在中间表的外键字段名称。 many-to-many: 作用:用于映射多对多的关系。 属性: class:对方的实体类名称 column:对方在中间表的外键字段名称 --> <set name="users" table="user_role" > <key column="role_id"></key> <many-to-many class="SysUser" column="user_id"></many-to-many> </set> </class> </hibernate-mapping>
在Hibernate.cfg.xml中添加映射文件
<!-- 3.配置映射文件的位置 --> <mapping resource="com/zwd/domain/SysRole.hbm.xml"/> <mapping resource="com/zwd/domain/SysUser.hbm.xml"/>
逆向生成数据库表:sys_user、sys_role、user_role(只有两个外键,组成联合主键)
public static void main(String[] args) { getCurrentSession(); }
6.多对多的保存和删除操作
保存:
/**
* 保存操作:
* 需求:
* 创建2个用户和3个角色
* 让1号用户具备1号和2号角色
* 让2号用户具备2号和3号角色
* 保存用户和角色。
*/
@Test
public void test1(){
//1、创建用户和角色
SysUser u1 = new SysUser();
u1.setUserName("用户1");
SysUser u2 = new SysUser();
u2.setUserName("用户2");
SysRole r1 = new SysRole();
r1.setRoleName("角色1");
SysRole r2 = new SysRole();
r2.setRoleName("角色2");
SysRole r3 = new SysRole();
r3.setRoleName("角色3");
//2、建立用户和角色之间的双向关系
//先建立用户的
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
//在建立角色的
r1.getUsers().add(u1);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
r3.getUsers().add(u2);
//3.保存:此处需要让多对多的关系中的其中任意一方放弃维护关系的权利。
// 否则:user和role都会向关联表中插入记录
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
session.save(u1);
session.save(u2);
session.save(r1);
session.save(r2);
session.save(r3);
tx.commit();
}
删除:
/**
* 删除操作:在项目中级联删除是禁止使用的
*/
@Test
public void test2(){
Session session = HibernateUtil.getCurrentSession();
Transaction tx = session.beginTransaction();
SysUser user = session.get(SysUser.class, 8l);
session.delete(user);//配置级联删除
tx.commit();
}
Hibernate第四天-JPA
1、JPA概述
JPA:它是java persistence api,java持久化规范。
JPA通过JDK5.0 注解或XML描述对象-关系表的映射关系,并将运行期间的实体对象持久化到数据库中。
JPA是一套ORM规范,hibernate实现了JAP规范。
hibernate中有自己独立的ORM操作数据库的方式,也有JPA规范实现的操作数据库的方式。
2、JPA环境搭建
-
创建java project项目,并导入jar包,required + jpa-metamodel-generator + 数据库驱动
-
在src下创建 folder,META-INF;在META-INF下创建
persistence.xml
。 -
增加persistence.xml配置文件中的提示。在hibernate的包中找到persistence_2_1.xsd的位置(
\hibernate-release-5.3.9.Final\lib\jpa-metamodel-generator\hibernate-jpamodelgen-5.3.9.Final\persistence_2_1.xsd
),Windows-preferences-XML Catalog,然后add,选择persistence_2_1.xsd的位置,key为http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
-
persistence.xml
导入约束:由于persistence_2_1.xsd会报错:找不到元素 ‘persistence’ 的声明,将2.1改为2.0解决问题。<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" > </persistence>
-
persistence.xml中的完整配置
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0" > <!-- 配置持久化单元,可以配置多个,但是名称不能重复。 name:用于指定持久化单元名称 transaction-type:指定事务类型 取值: JTA: Java Transaction API (一般用于集群下的事务) RESOURCE-LOCAL:指的是本地代码事务。(我们用这个)--> <persistence-unit name="myJPAUnit" transaction-type="RESOURCE_LOCAL"> <!-- JAP规范的提供商,可以不写 --> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <!-- 指定由JPA注解的实体类位置,可以不写 --> <class>com.zwd.domain.Customer</class> <!-- 连接数据库的相关配置,都是用hibernate的,所以只需把之前的hibernate主配置文件中的部分内容拷过来即可 --> <properties> <property name="" value=""/> <!-- 数据库方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/> <!-- 连接数据的信息 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/spring"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.connection.password" value="root"/> <!-- 2.Hibernate的可选配置 --> <!-- 配置数据库连接池 c3p0 :记得导c3p0的包(在hibernate包的option中) <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>--> <!-- 是否生成sql语句打印 --> <property name="hibernate.show_sql" value="true"/> <!-- 是否格式化生成sql语句 --> <property name="hibernate.format_sql" value="false"/> <!-- 采用怎样的方式处理ddl --> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence>
-
创建实体类Customer.java,并使用JPA的注解
package com.zwd.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /******************************************************************************* * javaBeans * cst_customer --> CstCustomer * <table explanation> * @author 2019-08-16 10:45:34 * * 使用的注解都是使用JPA规范,都需要导入javax.persistence下的包 */ @Entity//表明该类是一个实体类 @Table(name="cst_customer")//建立当前类和数据库表的对应关系 public class Customer implements java.io.Serializable { @Id//表明当前字段是主键 @Column(name="cust_id")//表明对应数据库的主键字段是cust_id @GeneratedValue(strategy=GenerationType.IDENTITY)//指定主键生产策略。strategy:使用JPA中提供的主键生产策略。此属性用不了。generator:可以使用hibernate中的主键生产策略。 private int custId; @Column(name="cust_name") private String custName; @Column(name="cust_source") private String custSource; @Column(name="cust_indestry") private String custIndustry; @Column(name="cust_level") private String custLevel; @Column(name="cust_address") private String custAddress; @Column(name="cust_phone") private String custPhone; //method public int getCustId() { return custId; } public void setCustId(int custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } //override toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'custId':'"+this.getCustId()+"',"); sb.append("'custName':'"+this.getCustName()+"',"); sb.append("'custSource':'"+this.getCustSource()+"',"); sb.append("'custIndustry':'"+this.getCustIndustry()+"',"); sb.append("'custLevel':'"+this.getCustLevel()+"',"); sb.append("'custAddress':'"+this.getCustAddress()+"',"); sb.append("'custPhone':'"+this.getCustPhone()+"'"); sb.append("}"); return sb.toString(); } }
-
创建JPA的工具类:JPA的factory:
EntityManagerFactory
;JPA操作数据库的对象:EntityManager
package com.zwd.uitls; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; /** * JPA的工具类 * @author Administrator * */ public class JPAUtil { //它就相当于sessionFactory private static EntityManagerFactory factory; static{ factory = Persistence.createEntityManagerFactory("myJPAUnit"); } /** * 获取JPA操作数据库的对象 * @return */ public static EntityManager CreateEntityManager(){ return factory.createEntityManager(); } public static void main(String[] args) { CreateEntityManager(); } }
3、JPA的CRUD操作
保存:
entityManager.persist(c);
相当于save();
/**
* 1、保存:entityManager.persist(c);
*/
@Test
public void test1(){
//创建客户对象
Customer c = new Customer();
c.setCustName("JPA保存1");
//1、获取EntityManager对象
EntityManager entityManager = JPAUtil.CreateEntityManager();
//2、获取事务,并开启
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3、操作数据库:保存
entityManager.persist(c);
//4、提交事务
tx.commit();
//5、关闭连接
entityManager.close();
}
-
查询一个(立即加载):
entityManager.find(Customer.class, 1);
相当于get();/** * 2、查询一个:entityManager.find(Customer.class, 1); */ @Test public void test2(){ //1、获取EntityManager对象 EntityManager entityManager = JPAUtil.CreateEntityManager(); //2、获取事务,并开启 EntityTransaction tx = entityManager.getTransaction(); tx.begin(); //3、操作数据库:查询一个 Customer c = entityManager.find(Customer.class, 1); System.out.println(c); //4、提交事务 tx.commit(); //5、关闭连接 entityManager.close(); }
-
查询一个(延迟加载):
entityManager.getReference(Customer.class, 1);
相当于load();/** * 2、查询一个(延迟加载):entityManager.getReference(Customer.class, 1);----load() */ @Test public void test2_1(){ //1、获取EntityManager对象 EntityManager entityManager = JPAUtil.CreateEntityManager(); //2、获取事务,并开启 EntityTransaction tx = entityManager.getTransaction(); tx.begin(); //3、操作数据库:查询一个(延迟加载) Customer c = entityManager.getReference(Customer.class, 1); System.out.println(c); //4、提交事务 tx.commit(); //5、关闭连接 entityManager.close(); }
更新
-
更新1:
更新1:在更改持久类的对象后,在tx.commit();时会自动更新数据库的记录
/** * 3、更新1:在更改持久类的对象后,在tx.commit();时会自动更新数据库的记录 */ @Test public void test3(){ //1、获取EntityManager对象 EntityManager entityManager = JPAUtil.CreateEntityManager(); //2、获取事务,并开启 EntityTransaction tx = entityManager.getTransaction(); tx.begin(); //3、操作数据库:查询一个后更新 Customer c = entityManager.find(Customer.class, 1); c.setCustAddress("播州区"); //4、提交事务 tx.commit(); //5、关闭连接 entityManager.close(); }
-
更新2:
更新2:entityManager.merge(c);
相当于update(); update如果遇到一级缓存已经包含了一个相同OID的对象会报错。merge则可以执行成功。/** * 4、更新2:entityManager.merge(c); */ @Test public void test4(){ //1、获取EntityManager对象 EntityManager entityManager = JPAUtil.CreateEntityManager(); //2、获取事务,并开启 EntityTransaction tx = entityManager.getTransaction(); tx.begin(); //3、操作数据库:查询一个后更新 Customer c = entityManager.find(Customer.class, 1); c.setCustAddress("遵义市播州区"); //更新 entityManager.merge(c); //4、提交事务 tx.commit(); //5、关闭连接 entityManager.close(); }
@Test public void test(){ Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); Customer c1 = session.get(Customer.class, 3); tx.commit(); session.close();//关闭 //修改客户信息 c1.setCustName("JPA1");//托管态 Session session1 = HibernateUtil.getSession(); Transaction tx1 = session1.beginTransaction(); //再次查询客户 Customer c2 = session1.get(Customer.class, 3); //更新修改对象: /**此处应该使用merge:因为session1中已经存在id为2的Customer对象c2, *当托管态的c1进入session1时,因为c1和c2的内容不一样, *所以当c1进入session1中时会被认为是不同的对象,但是ID又一样,所以使用update会报错:session中存在具有相同ID的不同对象 *使用merge将会合并两对象的内容,并更新c2的内容。 */ //session1.update(c1);//将托管态转为持久态 session1.merge(c1); tx1.commit(); session1.close(); }
删除:
entityManager.remove(c);
相当于delete();
/**
* 5、删除:entityManager.remove(c);
*/
@Test
public void test5(){
//1、获取EntityManager对象
EntityManager entityManager = JPAUtil.CreateEntityManager();
//2、获取事务,并开启
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3、操作数据库:查询一个后删除(需要把要删除对象查询出来)
Customer c = entityManager.find(Customer.class, 1);
//更新
entityManager.remove(c);
//4、提交事务
tx.commit();
//5、关闭连接
entityManager.close();
}
查询所有:
涉及的对象:JPA的Query
如何获取该对象:entityManager.createQuery(String jpql);
参数含义:
jpql:java persistence query language
它的写法和HQL很相似,也是把表名换成类名,将字段名换成属性名。
它在写查询所有时,不能直接用 from 实体类,
需要使用select关键字:select c from Customer c(也不能使用select *)
/**
* 6、查询所有:
* 涉及的对象:JPA的Query
* 如何获取该对象:entityManager.createQuery(String jpql);
* 参数含义:
* jpql:java persistence query language
* 它的写法和HQL很相似,也是把表名换成类名,将字段名换成属性名。
* 它在写查询所有时,不能直接用 from 实体类,
* 需要使用select关键字:select c from Customer c(也不能使用select *)
*/
@Test
public void test6(){
//1、获取EntityManager对象
EntityManager entityManager = JPAUtil.CreateEntityManager();
//2、获取事务,并开启
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
//3、操作数据库:查询所有
//Query query = entityManager.createQuery("select c from Customer c");
Query query = entityManager.createQuery("select c from Customer c where custId = ?2 and custName like ?3");
query.setParameter(2, 2);
query.setParameter(3, "%JPA%");
//获取数据集
List resultList = query.getResultList();
for (Object object : resultList) {
System.out.println(object);
}
//4、提交事务
tx.commit();
//5、关闭连接
entityManager.close();
}
4、JPA中的一对多关系映射操作
- 客户实体类:
Customer.java
//一对多的映射,一的一方:一个客户包含多个联系人
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL,
fetch=FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<LinkMan>(0);
//mappedBy="customer":表明Customer放弃维护关系的权利
//cascade=CascadeType.ALL:级联操作
//fetch=FetchType.EAGER:立即加载关联对象
package com.zwd.domain;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/*******************************************************************************
* javaBeans
* cst_customer --> CstCustomer
* <table explanation>
* @author 2019-08-16 10:45:34
*
* 使用的注解都是使用JPA规范,都需要导入javax.persistence下的包
*/
@Entity//表明该类是一个实体类
@Table(name="cst_customer")//建立当前类和数据库表的对应关系
public class Customer implements java.io.Serializable {
@Id//表明当前字段是主键
@Column(name="cust_id")//表明对应数据库的主键字段是cust_id
@GeneratedValue(strategy=GenerationType.IDENTITY)//指定主键生产策略。strategy:使用JPA中提供的主键生产策略。此属性用不了。generator:可以使用hibernate中的主键生产策略。
private int custId;
@Column(name="cust_name")
private String custName;
@Column(name="cust_source")
private String custSource;
@Column(name="cust_indestry")
private String custIndustry;
@Column(name="cust_level")
private String custLevel;
@Column(name="cust_address")
private String custAddress;
@Column(name="cust_phone")
private String custPhone;
//一对多的映射,一的一方:一个客户包含多个联系人
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<LinkMan>(0);
//method
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
public int getCustId() {
return custId;
}
public void setCustId(int custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
//override toString Method
public String toString() {
StringBuffer sb=new StringBuffer();
sb.append("{");
sb.append("'custId':'"+this.getCustId()+"',");
sb.append("'custName':'"+this.getCustName()+"',");
sb.append("'custSource':'"+this.getCustSource()+"',");
sb.append("'custIndustry':'"+this.getCustIndustry()+"',");
sb.append("'custLevel':'"+this.getCustLevel()+"',");
sb.append("'custAddress':'"+this.getCustAddress()+"',");
sb.append("'custPhone':'"+this.getCustPhone()+"'");
sb.append("}");
return sb.toString();
}
}
-
联系人实体类:
LinkMan.java
//一对多关系映射:多的一方 //从表的实体类应该包含主表实体类的对象引用。 @ManyToOne(targetEntity=Customer.class,fetch=FetchType.LAZY) @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer; // @JoinColumn:为联系人实体类添加外键
package com.zwd.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; /******************************************************************************* * javaBeans * cst_linkman --> CstLinkman * <table explanation> * @author 2019-08-12 21:30:10 * */ @Entity @Table(name="cst_linkman") public class LinkMan implements java.io.Serializable { /** 联系人编号(主键) **/ @Id @Column(name="lkm_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private long lkmId; /** 联系人姓名 **/ @Column(name="lkm_name") private String lkmName; /** 联系人性别 **/ @Column(name="lkm_gender") private String lkmGender; /** 联系人办公电话 **/ @Column(name="lkm_phone") private String lkmPhone; /** 联系人手机 **/ @Column(name="lkm_mobile") private String lkmMobile; /** 联系人邮箱 **/ @Column(name="lkm_email") private String lkmEmail; /** 联系人职位 **/ @Column(name="lkm_position") private String lkmPosition; /** 联系人备注 **/ @Column(name="lkm_memo") private String lkmMemo; //一对多关系映射:多的一方 //从表的实体类应该包含主表实体类的对象引用。 @ManyToOne(targetEntity=Customer.class,fetch=FetchType.LAZY) @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } //method public long getLkmId() { return lkmId; } public void setLkmId(long lkmId) { this.lkmId = lkmId; } public String getLkmName() { return lkmName; } public void setLkmName(String lkmName) { this.lkmName = lkmName; } public String getLkmGender() { return lkmGender; } public void setLkmGender(String lkmGender) { this.lkmGender = lkmGender; } public String getLkmPhone() { return lkmPhone; } public void setLkmPhone(String lkmPhone) { this.lkmPhone = lkmPhone; } public String getLkmMobile() { return lkmMobile; } public void setLkmMobile(String lkmMobile) { this.lkmMobile = lkmMobile; } public String getLkmEmail() { return lkmEmail; } public void setLkmEmail(String lkmEmail) { this.lkmEmail = lkmEmail; } public String getLkmPosition() { return lkmPosition; } public void setLkmPosition(String lkmPosition) { this.lkmPosition = lkmPosition; } public String getLkmMemo() { return lkmMemo; } public void setLkmMemo(String lkmMemo) { this.lkmMemo = lkmMemo; } //override toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'1kmId':'"+this.getLkmId()+"',"); sb.append("'lkmName':'"+this.getLkmName()+"',"); sb.append("'lkmGender':'"+this.getLkmGender()+"',"); sb.append("'1kmPhone':'"+this.getLkmPhone()+"',"); sb.append("'lkmMobile':'"+this.getLkmMobile()+"',"); sb.append("'lkmEmail':'"+this.getLkmEmail()+"',"); sb.append("'lkmPosition':'"+this.getLkmPosition()+"',"); sb.append("'lkmMemo':'"+this.getLkmMemo()+"'"); sb.append("}"); return sb.toString(); } }
保存:
此时只需要配置谁拥有维护关系的权利即可。mappedBy="customer"
:表明Customer放弃维护关系的权利
/**
* 1、保存操作:在Customer中配置mappedBy="customer"
* 创建新的客户和联系人
* 建立双向的关联关系
* 保存
*/
@Test
public void test1(){
//创建新的客户和联系人
Customer c = new Customer();
LinkMan l = new LinkMan();
c.setCustName("JPA One To Many customer");
l.setLkmName("JPA One To Many linkMan");
//建立双向关联关系
c.getLinkMans().add(l);
l.setCustomer(c);
EntityManager em = JPAUtil.CreateEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//保存
em.persist(c);
em.persist(l);
tx.commit();
em.close();
}
更新:
在更新客户的时候同时保存联系人。在客户的关系中配置 cascade=CascadeType.PERSIST
/**
* 级联更新:在Customer中配置cascade=CascadeType.PERSIST
* 新建一个联系人,查询一个客户
* 为该客户分配联系人
* 更新客户
*/
@Test
public void test2(){
//创建新的客户和联系人
LinkMan l = new LinkMan();
l.setLkmName("JPA One To Many linkMan2");
EntityManager em = JPAUtil.CreateEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = em.find(Customer.class, 4);
//建立双向关联关系
c.getLinkMans().add(l);
l.setCustomer(c);
em.merge(c);//级联更新,在更新客户的同时保存联系人
tx.commit();
em.close();
}
删除:
删除一的方需要级联删除:在客户的关系中配置:cascade=CascadeType.REMOVE/ALL
/**
* 删除:
* 在删除多的一方相当于单表操作。
* 在删除一的一方需要级联操作。
* 在客户中配置: cascade=CascadeType.REMOVE/ALL
*/
@Test
public void test3(){
EntityManager em = JPAUtil.CreateEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer c = em.find(Customer.class, 7);
//级联删除:@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL)
em.remove(c);
tx.commit();
em.close();
}
查询
-
查询一的方
/** * 查询一的方(find),多的方默认为延迟加载:先查询客户,然后根据客户获取联系人 * 默认情况: * 在find客户时只查询客户,在get联系人对象时,在第一次用到的联系人对象时才查询 * 也可以将联系人的查询改为立即查询(在find客户时就查询联系人) * 在客户的联系人属性中配置 :fetch=FetchType.EAGER * @OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL,fetch=FetchType.EAGER) */ @Test public void test4(){ EntityManager em = JPAUtil.CreateEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer c = em.find(Customer.class, 8); System.out.println(c); Set<LinkMan> linkMans = c.getLinkMans(); System.out.println(linkMans); tx.commit(); em.close(); }
-
查询多的方:
/** * 查询多的方(find),一的方默认为立即加载:先查询联系人,然后根据联系获取客户 * 默认情况:在find联系人时会立即查询客户 * 也可以改为延迟加载: * 在联系人的关系映射中配置: @ManyToOne(targetEntity=Customer.class,fetch=FetchType.LAZY) */ @Test public void test5(){ EntityManager em = JPAUtil.CreateEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); LinkMan l = em.find(LinkMan.class, 6l); System.out.println(l); Customer c = l.getCustomer(); System.out.println(c); tx.commit(); em.close(); }
5、JPA中的多对多映射操作
案例:用户和角色
-
用户:
SysUser.java
//多对多关系映射:一个用户对应多个角色 @ManyToMany(mappedBy="users",cascade=CascadeType.ALL)//多对多关系,user放弃维护关系的权利 Set<SysRole> roles = new HashSet<SysRole>(0);
JAP中主键生产的两种方式:
@Id @Column(name="user_id") // @GenericGenerator(name="aa",strategy="identity")//指定一个hibernate的主键生产器 // @GeneratedValue(generator="aa")//用hibernate的生成器 @GeneratedValue(strategy=GenerationType.IDENTITY) private long userId;
package com.zwd.domain; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="sys_user") public class SysUser implements Serializable{ @Id @Column(name="user_id") // @GenericGenerator(name="aa",strategy="identity")//指定一个hibernate的主键生产器 // @GeneratedValue(generator="aa")//用hibernate的生成器 @GeneratedValue(strategy=GenerationType.IDENTITY) private long userId; @Column(name="user_name") private String userName; @Column(name="user_password") private String userPassWord; //多对多关系映射:一个用户对应多个角色 @ManyToMany(mappedBy="users",cascade=CascadeType.ALL)//多对多关系,user放弃维护关系的权利 Set<SysRole> roles = new HashSet<SysRole>(0); public Set<SysRole> getRoles() { return roles; } public void setRoles(Set<SysRole> roles) { this.roles = roles; } public long getUserId() { return userId; } public void setUserId(long userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassWord() { return userPassWord; } public void setUserPassWord(String userPassWord) { this.userPassWord = userPassWord; } @Override public String toString() { return "SysUser [userId=" + userId + ", userName=" + userName + ", userPassWord=" + userPassWord + "]"; } }
-
角色:
SysRole.java
//多对多关系映射:一个角色对应多个用户 @ManyToMany(cascade=CascadeType.ALL)//多对多:role需要维护关系 //加入一张表 @JoinTable(name="user_role_ref",//表名 joinColumns = {@JoinColumn(name="role_id",referencedColumnName="role_id")},//当前实体类在中间表中的外键字段 inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")})//对方实体类在中间表中的外键字段 Set<SysUser> users = new HashSet<SysUser>(0);
package com.zwd.domain; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="sys_role") public class SysRole implements Serializable{ @Id @Column(name="role_id") //@GenericGenerator(name="aa",strategy="identity") //@GeneratedValue(generator="aa") @GeneratedValue(strategy=GenerationType.IDENTITY) private long roleId; @Column(name="role_name") private String roleName; @Column(name="role_memo") private String roleMemo; //多对多关系映射:一个角色对应多个用户 @ManyToMany(cascade=CascadeType.ALL)//多对多:role需要维护关系 //加入一张表 @JoinTable(name="user_role_ref",//表名 joinColumns = {@JoinColumn(name="role_id",referencedColumnName="role_id")},//当前实体类在中间表中的外键字段 inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")})//对方实体类在中间表中的外键字段 Set<SysUser> users = new HashSet<SysUser>(0); public Set<SysUser> getUsers() { return users; } public void setUsers(Set<SysUser> users) { this.users = users; } public long getRoleId() { return roleId; } public void setRoleId(long roleId) { this.roleId = roleId; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleMemo() { return roleMemo; } public void setRoleMemo(String roleMemo) { this.roleMemo = roleMemo; } @Override public String toString() { return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]"; } }
保存:
/**
* 1、保存:
* 创建两个用户
* 创建三个角色
* 让1号用户具备1号和2号角色
* 让2号用户具备2号和3号角色
* 可以一个一个保存:
* em.persist(u1);
em.persist(u2);
em.persist(r1);
em.persist(r2);
em.persist(r3);
也可以级联保存:在双向关系中配置:cascade=CascadeType.PERSIST
*/
@Test
public void test1(){
//创建用户和角色
SysUser u1 = new SysUser();
SysUser u2 = new SysUser();
SysRole r1 = new SysRole();
SysRole r2 = new SysRole();
SysRole r3 = new SysRole();
u1.setUserName("JPA Many To Many u1");
u2.setUserName("JPA Many To Many u2");
r1.setRoleName("JPA Many To Many r1");
r2.setRoleName("JPA Many To Many r2");
r3.setRoleName("JPA Many To Many r3");
//建立用户和角色的双向关联关系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r1.getUsers().add(u1);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
r3.getUsers().add(u2);
EntityManager em = JPAUtil.CreateEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//保存操作
// em.persist(u1);
// em.persist(u2);
// em.persist(r1);
// em.persist(r2);
// em.persist(r3);
//级联保存
em.persist(u1);
tx.commit();
em.close();
}
删除:
/**
* 级联删除:无论在hibernate还是在JPA中都不允许
* cascade=CascadeType.ALL
*/
@Test
public void test2(){
EntityManager em = JPAUtil.CreateEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
SysUser user = em.find(SysUser.class,1l);
em.remove(user);
tx.commit();
em.close();
}
6、在JPA中使用c3p0连接池
-
导c3p0的jar包
-
配置
persistence.xml
<!-- 配置数据库连接池 c3p0 :记得导c3p0的包(在hibernate包的option中)--> <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
/** * 验证c3p0的配置是否可用 */ @Test public void test3(){ EntityManager em = JPAUtil.CreateEntityManager(); Session session = em.unwrap(Session.class); session.doWork(new Work(){ @Override public void execute(Connection conn) throws SQLException { System.out.println(conn.getClass().getName()); } }); }
7、使用JPA的注解配置,使用hibernate的xml配置文件和操作
-
Customer.java
: 不使用hibernate的映射配置,使用JPA的注解配置package com.zwd.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; /******************************************************************************* * javaBeans * cst_customer --> Customer * <table explanation> * @author 2019-08-12 14:43:39 * */ @Entity @Table(name="cst_customer") public class Customer implements java.io.Serializable { @Id @Column(name="cust_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int custId; @Column(name="cust_name") private String custName; @Column(name="cust_source") private String custSource; @Column(name="cust_industry") private String custIndustry; @Column(name="cust_level") private String custLevel; @Column(name="cust_address") private String custAddress; @Column(name="cust_phone") private String custPhone; //method public int getCustId() { return custId; } public void setCustId(int custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } //override toString Method public String toString() { StringBuffer sb=new StringBuffer(); sb.append("{"); sb.append("'custId':'"+this.getCustId()+"',"); sb.append("'custName':'"+this.getCustName()+"',"); sb.append("'custSource':'"+this.getCustSource()+"',"); sb.append("'custIndustry':'"+this.getCustIndustry()+"',"); sb.append("'custLevel':'"+this.getCustLevel()+"',"); sb.append("'custAddress':'"+this.getCustAddress()+"',"); sb.append("'custPhone':'"+this.getCustPhone()); sb.append("}"); return sb.toString(); } }
-
Hibernate.cfg.xml
:配置映射文件时写成类,其他不变<!-- 3.配置映射文件的位置 --> <!-- <mapping resource="com/zwd/domain/Customer.xml" /> <mapping resource="com/zwd/domain/LinkMan.xml" /> --> <mapping class="com.zwd.domain.Customer"/>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 1.配置连接的数据库信息 --> <!-- 数据库方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property> <!-- 连接数据的信息 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/spring</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">root</property> <!-- 2.Hibernate的可选配置 --> <!-- 配置数据库连接池 c3p0 :记得导c3p0的包(在hibernate包的option中)--> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- 配置session与线程绑定 --> <property name="hibernate.current_session_context_class">thread</property> <!-- 是否生成sql语句打印 --> <property name="hibernate.show_sql">true</property> <!-- 是否格式化生成sql语句 --> <property name="hibernate.format_sql">false</property> <!-- 采用怎样的方式处理ddl --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 3.配置映射文件的位置 --> <!-- <mapping resource="com/zwd/domain/Customer.xml" /> <mapping resource="com/zwd/domain/LinkMan.xml" /> --> <mapping class="com.zwd.domain.Customer"/> </session-factory> </hibernate-configuration>
-
对数据库的操作使用hibernate的操作
package com.zwd.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.zwd.domain.Customer; import com.zwd.utils.HibernateUtil; /** * 使用JPA的注解,hibernate的配置文件和操作 * @author Administrator * */ public class HibernateDemo1 { /** * 保存 */ @Test public void test1(){ Customer c = new Customer(); c.setCustName("JPA Hibernate 保存"); Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); session.save(c); tx.commit(); } /** * 查询一个 */ @Test public void test2(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Customer customer = session.find(Customer.class, 9); System.out.println(customer); tx.commit(); } /** * 更新 */ @Test public void test3(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Customer customer = session.find(Customer.class, 9); customer.setCustPhone("123455"); session.update(customer); tx.commit(); } /** * 删除 */ @Test public void test4(){ Session session = HibernateUtil.getCurrentSession(); Transaction tx = session.beginTransaction(); Customer customer = session.find(Customer.class, 9); session.delete(customer); tx.commit(); } }