Java框架之Hibernate(一)
一、Hibernate - 核心接口
它是 JBoss Community team (社区团队) 开发的。Hibernate 是一个开源的,对象关系模型框架 (ORM),它对JDBC进行了轻量的封装, 使java程序员可以面对象的方式进行数据库操作。Hibernate 一共有5个核心接口
1.Session
用于连接数据库,相当于 jdbc 中的 Connection (它和 servlet 中 的Session 一点关系也没有),负责执行对象的CRUD操作 ,它是非线程安全的。
2.SessionFactory //用于得到 Session ,相当于jdbc中 DriverManager
3.Transaction //用于事务管理
4.Query 和 Criteria //主要用于查询数据
5.Configuration //用于得到配置信息
二、Hibernate - 类库简介
Hibernate3.jar
antlr-2.7.6.jar(必需):Hibernate使用ANTLR来产生查询分析器,这个类库在运行环境下时也是必需的。
dom4j(必需):Hibernate使用dom4j解析XML配置文件和XML映射元文件。
commons-collections-3.1,CommonsLogging(必需):Hibernat使用ApacheJakartaCommons项目提供的多个工具类库。
dom4j-1.6.1(可选):Hibernate使用CommonsLoggingAPI,它也可以依次使用Log4j作为底层实施log的机制。
如果上下文类目录中存在Log4j库,则CommonsLogging使用Log4j和并它在上下文类路径中寻找的log4j.properties文件。
你可以使用在Hibernate发行包中包含中的那个示例Log4j的配置文件。这样,把log4j.jar和它的配置文件(位于src/目录中)拷贝到你的上下文类路径下,就可以在后台看到底程序如何运行的。
javassist-3.12.0.GA 一个开源的分析、编辑和创建Java字节码的类库
slf4j-api-1.6.1 为java提供的简单日志Facade。Facade:门面,更底层一点说就是接口
jta-1.1.jar Java事务API( Java Transaction API )
其他文件是不是必需的:请察看Hibernate发行包中的lib/README.txt文件,这是一个Hibernate发行包中附带的第三方类库的列表,他们总是保持最新的。你可以在那里找到所有必需或者可选的类库(注意:其中的 "buildtimerequired" 指的是编译Hibernate时所需要而非编译你自己的程序所必需的类库)。
三、第一个Hibernate 程序
1) 导包
2) 配置文件
1)Hibernate 的主配置文件
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE Hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://Hibernate.sourceforge.net/Hibernate-configuration-3.0.dtd"> <Hibernate-configuration> <session-factory> <property name="show_sql">true</property> //把生成的sql显示出来 <property name="dialect">org.Hibernate.dialect.MySQLDialect</property> //指定数据库用的方言 <property name="connection.url">jdbc:mysql://localhost:3306/shop</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="myeclipse.connection.profile">mysql_cat</property> <mapping resource="cat/beans/AdminInfo.hbm.xml" /> //引入一个映射文件 </session-factory> </Hibernate-configuration>
可以查找 Hibernate-3.6.10.Final\project\etc\Hibernate.properties 这个文件,找到相应的配置项和内容。
2) 实体类
public class AdminInfo { private int id; private String adminName; private String password; private String note; ... get set 方法 }
3) 生成实体类的映射文件 AdminInfo.hbm.xml
<?xml version="1.0"?> <!DOCTYPE Hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://Hibernate.sourceforge.net/Hibernate-mapping-3.0.dtd"> <Hibernate-mapping package="cat.beans"> <class name="AdminInfo"> <id name="id" > <generator class="native"/> <!-- 指明主键的生成策略 --> </id> <property name="adminName" /> <property name="password" /> <property name="note" /> </class> </Hibernate-mapping>
3)测试
public class Test { public static void main(String[] args) { test(); } //可能出现的问题 //1 Unknown entity: cat.beans.AdminInfo 原因是没有把映射文件引入到主配置文件 //2 为什么没有真正的添到数据库中呢,因为没有开启事务 public static void test(){ //读配置文件 Configuration cxf=new Configuration(); cxf.configure(); //会去读配置文件,扩号中可以写上配置文件的名称,也可以不写,如果不写,默认读 Hibernate.cfg.xml 这个文件 //创建SessionFactory对象 SessionFactory sessionFactory=cxf.buildSessionFactory(); //创建Session对象 Session session= sessionFactory.openSession(); //开启事务 Transaction tx= session.beginTransaction(); AdminInfo admin=new AdminInfo(); admin.setAdminName("管理员Hibernate"); admin.setPassword("adminaaa"); admin.setNote("这是第一个用户用Hibernate添加的"); session.save(admin); //提交事务 tx.commit(); session.close(); System.out.println("操作成功"); System.out.println(admin.getId()); //它居然能得到id } }
说明: 对于 beans 中的 实体类(AdminInfo) ,没有什么特别的要求(但必须要有get 和set ),但必须有一个无参的构造方法,最好不要声明为 final 的, 对懒加载有影响。对映射文件的要求和简单说。
1) 通常,命名是 类名.hbm.xml => AdminInfo.hbm.xml
2) 可以在一个映射文件中,映射多个类,但通常不推荐这么做
3)
<Hibernate-mapping package="cat.beans"> //这里的package可以不写,但如果不写,下面的 要写成全类名 <class name="AdminInfo" > 如果上面的package不写,这里要写成 <class name="cat.beans.AdminInfo" > ....
4) 一些属性是有默认值的,可以明确的指定值,也可以使用默认值
<class name="AdminInfo" table="adminInfo" > <id name="id" > <generator class="native" /> //native 主键生成器,会根据不同的数据库选择不同的主键生成方式 </id> <property name="adminName" column="aminName" type="string"/> <property name="password" /> <property name="note" /> </class>
四、工具类和标准的代码
工具类代码实例
package cat.hibutils; import org.Hibernate.Session; import org.Hibernate.SessionFactory; import org.Hibernate.cfg.Configuration; public class HibUtil { private HibUtil(){} //防止别人创建本类的实例 private static SessionFactory _factory; static{ Configuration cxf=new Configuration(); cxf.configure(); _factory=cxf.buildSessionFactory(); } //得到Session //Session 对象是线程不安全的 public static Session getSession(){ return _factory.openSession(); } //得到 SessionFactory public SessionFactory getSessionFactory(){ return _factory; } //关闭连接 public static void close(Session s){ if(s!=null){ s.close(); } } } //标准的添加方法 public static void main(String[] args) { AdminInfo admin=new AdminInfo(); admin.setAdminName("标准方法添的用户"); admin.setPassword("123"); add(admin); System.out.println("用户添加成功"); } //标准的add方法 public static void add(AdminInfo admin){ Session s=null; Transaction tx=null; try{ s=HibUtil.getSession(); tx=s.beginTransaction(); s.save(admin); tx.commit(); } catch(Exception ex){ if(tx!=null){ tx.rollback(); } ex.printStackTrace(); }finally{ HibUtil.close(s); } } //精简的写法 public static void simpleAdd(AdminInfo admin){ Session s=null; Transaction tx=null; try { s=HibUtil.getSession(); tx=s.beginTransaction(); s.save(admin); tx.commit(); } finally{ HibUtil.close(s); } }
五、使用 threadLocal
public class HibUtil { private HibUtil(){} //防止别人创建本类的实例 private static SessionFactory _factory; private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); static{ Configuration cxf=new Configuration(); cxf.configure(); _factory=cxf.buildSessionFactory(); } //得到Session //Session 对象是线程不安全的 public static Session getSession(){ //return _factory.openSession(); Session s=threadLocal.get(); if(s == null){ s=_factory.openSession(); threadLocal.set(s); } return s; } //得到 SessionFactory public SessionFactory getSessionFactory(){ return _factory; } //关闭连接 public static void closeSession(){ Session s=threadLocal.get(); threadLocal.set(null); if(s!=null){ s.close(); } } } public static void simpleAdd(AdminInfo admin){ Session s=null; Transaction tx=null; try { s=HibUtil.getSession(); tx=s.beginTransaction(); s.save(admin); tx.commit(); } finally{ HibUtil.closeSession(); //关连接的时候,可以这样关 } }
六、Session接口
几个常用的方法
1) save , persist 保存数据, persist 在事务外不会产生insert语句
2) delete 删除对象
3) update 更新一个对象,如果数据库中没有对应的记录,将出错
4) get 根据ID查询对象,会立刻访问数据库
5) load 根据ID查询对象 (返回的是代理对象,不会立即访问数据库,懒加载)
6) saveOrUpdate (根据ID和version来决定是要保存还是要更新) , merge (调用 merge 你的对象还是脱管的)
7) lock 把对象变成持久对象,但不会同步对象的状态
//例子,使用get查询用户 public static void main(String[] args) { AdminInfo admin=getAdmin(89); System.out.println(admin); } public static AdminInfo getAdmin(int id){ try{ Session s=HibUtil.getSession(); return (AdminInfo)s.get(AdminInfo.class, id); }finally{ HibUtil.closeSession(); } }
//例子,使用load方法进行查询 public static AdminInfo loadAdmin(int id){ try{ Session s=HibUtil.getSession(); AdminInfo admin= (AdminInfo)s.load(AdminInfo.class, id); return admin; //如果这里不直接访问 admin 对象,则返回的是代理,并不是真正查询出来的数据 }finally{ HibUtil.closeSession(); } } public static void main(String[] args) { AdminInfo admin=loadAdmin(89); System.out.println(admin.getAdminName()); //could not initialize proxy - no Session 不能初始化代理对象,因为没有session了 }
七、对象状态
1.瞬时 (transien) : 数据库中没有数据与之对应,超过作用域就失效,会被垃圾回收器回收。一般的 new 出来的对象,且与session无关
2.持久 (persistent): 数据库中有数据与之对应,与 Session有关联,而且相关的Session没有关闭,事务还没有提交, 持久对象的状态发生改变, 在事务提交的时候,会保存到数据库 (Hibernate会检测到)
3.脱管 (detached) ,数据库中有数据与之对应, 但当前没有Session与之关联, 脱管对象状态发生改变也不会影响到数据库
瞬时: 自由人
持久: 法庭上被审问中的人,有案底 , 所有的话都是呈堂证供
脱管: 审完了,放了的人 , 有案底,但言论自由
八、封装简单的操作到HibUtil
//增加 public static void add(Object obj){ Session s=null; Transaction tx=null; try{ s=getSession(); tx=s.beginTransaction(); s.save(obj); tx.commit(); }finally{ closeSession(); } } //删除 public static void del(Object obj){ try{ Session s=getSession(); Transaction tx=s.beginTransaction(); s.delete(obj); tx.commit(); }finally{ closeSession(); } } //修改 public static void update(Object obj){ Session s=null; Transaction tx=null; try{ s=getSession(); tx=s.beginTransaction(); s.update(obj); tx.commit(); }finally{ closeSession(); } } //查询 public static Object get(Class clazz, Serializable id){ try{ Session s=getSession(); return s.get(clazz, id); }finally{ closeSession(); } }
九、HQL
public class AdminDao { //根据用户名和密码查询用户 public AdminInfo getLoginAdmin(String adminName,String password){ try{ Session s=HibUtil.getSession(); String hql="from AdminInfo a where a.adminName= ? and a.password= ?"; //AdminInfo 必须是对象名,不能是表名 Query q=s.createQuery(hql); q.setString(0, adminName); //注意,它是从0开始的 q.setString(1, password); AdminInfo admin= (AdminInfo)q.uniqueResult(); //只返回一对象,如果返回的不是一条数据,将出现异常 query did not return a unique result 54 return admin; }finally{ HibUtil.closeSession(); } } //查询所有用户 @SuppressWarnings("unchecked") public List<AdminInfo> getAllAdmin(){ try{ Session s=HibUtil.getSession(); /* Query q =s.createQuery("from AdminInfo"); return q.list(); //返回一个列表 */ return s.createQuery("from AdminInfo").list(); }finally{ HibUtil.closeSession(); } } }
生成测试用例 :
在类上,右键, new 输入 other , 搜索 junit 然后一步步添加即可。
public class AdminDaoTest { private AdminDao dao; @BeforeClass //注解 public static void setUpBeforeClass() throws Exception { //主要用于对静态成员进行初始化 } @AfterClass public static void tearDownAfterClass() throws Exception { } @Before //表示在执行之前做的处理 public void setUp() throws Exception { dao=new AdminDao(); } @After //执行之后做的处理 public void tearDown() throws Exception { System.out.println("测试完毕"); //在每个方法执行完后都会调用 } @Test public void testGetLoginAdmin() { AdminInfo admin=dao.getLoginAdmin("aaaaaaa", "aaa"); if(admin==null){ System.out.println("没查到"); } else{ System.out.println(admin); } } @Test public void testGetAllAdmin() { List<AdminInfo> list=dao.getAllAdmin(); for (AdminInfo a:list) { System.out.println(a); } } }
HQL 的补充说明
1)上面的写法
String hql="from AdminInfo a where a.adminName= ? and a.password= ?";
也可以不用别名,如下:
String hql="from AdminInfo where adminName= ? and password= ?"
2) 可以使用命名参数
String hql="from AdminInfo a where a.adminName=:aname and a.password= :pwd "; //AdminInfo 必须是对象名,不能是表名 Query q=s.createQuery(hql); q.setString("aname", adminName); //注意,它是从0开始的 q.setString("pwd", password);
3) Query 接口有个种set 方法, 可以对指定类型的参数进行传入
4) 可以使用 setFirstResult 和 setMaxResults 进行结果集过滤,实现分页查询
public List<AdminInfo> getAdminList(int beginRow,int pageSize){ try{ Session s=HibUtil.getSession(); Query q=s.createQuery("from AdminInfo"); q.setFirstResult(beginRow); q.setMaxResults(pageSize); return q.list(); }finally{ HibUtil.closeSession(); } }
5) 查询数据行数
public int getAdminCount(){ try{ Session s=HibUtil.getSession(); Query q=s.createQuery("select count(*) from AdminInfo"); //后面是对象名, long count=(Long)q.uniqueResult(); return new Integer(count+""); }finally{ HibUtil.closeSession(); } }
补充:HQL 常见的查询
// 使where Query q=s.createQuery("from AdminInfo where id not between 10 and 20"); //查询id不在10 到20之间的 // 使用in Query q=s.createQuery("from AdminInfo where AdminName in('张三','李四','王五')"); //只要在这个('张三','李四','王五')集合中含有的,就查出来 // 使用 like Query q=s.createQuery("from AdminInfo where AdminName like %赵%"); // 查询名字中含有赵的所有用户 // 使用 null Query q=s.createQuery("from AdminInfo where note is null"); //查询所有备注为null的用户 // 使用 and Query q=s.createQuery("from AdminInfo where note is null and id<5"); //查询备注信息是null而且id<5的用户 // 执行删除 Query q=s.createQuery("from AdminInfo where password is null"); // 批量删除: public void delAdmins(String password){ try{ Session s=HibUtil.getSession(); Transaction tx=s.beginTransaction(); Query q=s.createQuery("delete from AdminInfo where password= :pwd"); q.setString("pwd", password); q.executeUpdate(); //执行删除 tx.commit(); }finally{ HibUtil.closeSession(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程