Hibernate学习笔记(二)

本文介绍Hibernate框架的高级使用技巧,包括记录修改方法、批量处理数据策略、检索策略的应用、HQL查询、集合过滤技术以及并发问题的处理方案。

一、修改一条记录

    更新数据库中的一条记录有两种方式:

    ① 加载持久化对象,修改对象属性,然后session清理缓存的时候会自动同步更新数据库中的数据。

    ② 更新游离对象,通过session的update方法更新数据库中的相应数据。


二、Hibernate与数据库触发器协同工作的问题及解决办法

    问题1:触发器使session缓存中的持久化对象与数据库中数据不一致。

    解决方法:执行完持久化操作后马上调用session的flush方法将改变同步到数据库,然后调用refresh方法重新加载持久化对象。

    问题2:session的update方法盲目激发触发器。

    产生问题的原因:使用update或者saveOrUpdate方法更新游离对象时,session无法判断对象属性是否和数据库中一致,为保险起见,session总是会将游离对象的属性更新到数据库中。实际上,如果游离对象的属性和数据库中是一致的,这条update语句就是多余的,触发器也不应该被激发。

    解决方法:在映射文件的<class>元素中设置select-before-update属性为true,从而在session的update和saveOrUpdate方法更新一个游离对象的时候,先执行查询语句,比较游离对象的属性和数据库是否一致,不一致的时候才执行更新语句。这样会避免盲目激发触发器,但是会有一定程度的效率损失。


三、批量处理数据

    一般说来,应该尽量避免在应用层进行批量操作,而应该在数据库层直接批量操作。如果批量操作的逻辑比较复杂,则应该通过运行存储过程来完成。

    在应用层进行批量操作主要有以下方式:

    ① 通过session批量操作

    通过session执行批量操作具有以下约束。

    1)需要在Hibernate配置文件中设置单次批量处理的数目,合理的取值通常是10~50。如 hibernate.jdbc.batch_size=20,并且保证每次向数据库发送的批量SQL语句数目与这个batch_size一致。

    2)如果采用identity标示符生成器,则hibernate无法在jdbc层进行批量插入操作。

    3)批量操作时,建议关闭hibernate二级缓存。


    批量插入数据的部分代码如下:

for(int i=0;i<100000;i++){
    Customer c = new Customer(...);
    session.save(c);
    if(i%20==0){
        session.flush();
        session.clear();
    }
}
    如前文所述,为保证上述代码顺利执行,需要遵循以下约束:hibernate.jdbc.batch_size=20;Customer对象标示符生成器不是“identity”;关闭二级缓存。


    批量更新数据的部分代码如下:

ScrollableResults customers = session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);
int count=0;
while(customers.next()){
    Customer c = (Customer) customers.get(0);
    c.setAge(c.getAge() + 1);
    if(++count % 20 == 0){
        session.flush();
        session.clear();
    }
}
    上述代码中,Query对象的scroll()方法返回的ScrollableResults对象实际不包含任何Customer对象,它仅包含用于在线定位数据库中的CUSTOMERS记录的游标。只有当程序遍历访问ScrollableResults对象中的特定元素时,它才会到数据库中加载相应的Customer对象。

    为保证程序顺利运行,需要遵守以下约束:

    Hibernate配置文件中,hibernate.jdbc.batch_size=20;

    关闭二级缓存。如果程序中已经开启了二级缓存,也可以通过以下方式在程序中忽略二级缓存

ScrollableResults customers = session.createQuery("from Customer").setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);

    ② 通过StatelessSession进行批量操作

    主要代码如下:

StatelessSession session = SessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();

ScrollableResults customers = session.createQuery("from Customer").scroll(ScrollMode.FORWARD_ONLY);
while(customers.next()){
    Customer c = (Customer) customers.get(0);
    c.setAge(c.getAge() + 1);
    session.update(c);
}

tx.commit();
session.close();
    StatelessSession与session用法相似,二者主要区别如下:

    1)前者没有缓存,通过StatelessSession加载、保存、更新的对象处于游离状态。

    2)前者不会与二级缓存交互。

    3)调用前者的方法时,sql语句立即执行,而后者需要等到清理缓存的时候才执行。

    4)前者不会进行脏检查,所以上述程序中修改内存中Customer对象的属性之后,还要调用update方法更新数据库中相应数据。而后者得到的是持久化对象,修改对象属性之后,hibernate会自动在session关闭前清理缓存同步数据。

    5)前者不会对任何关联对象进行级联操作。

    6)前者所做操作可以被Interceptor拦截器捕捉到,但是会被hibernate事件处理系统忽略。

    7)前者两次加载主键相同的对象时,获得的是两个内存地址不同的对象,用“==”比较获得的是false。


    ③通过HQL批量操作

    通过Query.executeUpdate方法批量更新或删除数据,自定义HQL语句做方法参数。


    ④通过jdbc API批量操作

    hibernate3中通过session.connection()获得数据库连接,之后可以通过jdbc API进行数据库操作。但是此方法已经被废弃,hibernate4中甚至已经删除了此方法,应该采用如下方法获取connection:

session.doWork(new Work() {
	@Override
	public void execute(Connection connection) throws SQLException {
		// TODO Auto-generated method stub
	}
});

    ⑤hibernate调用存储过程

    hibernate没有专门的接口调用存储过程,在需要的时候先获得connection,然后通过jdbc API调用存储过程。


四、Hibernate检索策略

检索策略作用域可选策略默认值受影响的检索方法
类级别立即检索
延迟检索
延迟检索session的load()方法
关联级别立即检索
延迟检索
迫切左外连接检索
延迟检索session的load()和get()方法,以及Query API和Criteria API。
例外情况是Query API会忽略映射文件中设定的迫切左外连接检索策略。

    ①类级别的检索策略

    如果程序加载一个持久化对象的目的是为了访问他的属性,可以使用立即检索。如果加载一个持久化对象的目的仅仅是获得它的引用,可以使用延迟检索。如下是适用延迟检索的情况:

Customer c = (Customer)session.load(Customer.class,new Long(1));
Order o = new Order();
o.setCustomer(c);
session.save(o);

    ②迫切左外连接

List result = session.createQuery("from Customer c left jion fetch c.orders o where c.name like 'T%'").list();
    使用迫切左外连接检索策略时,Query的list()方法返回的集合中存放Customer对象的引用,每个Customer对象的orders集合都被初始化,存放所有关联的Order对象。使用迫切左外连接检索策略时,查询结果中可能包含重复元素,可以通过HashSet进行过滤:

HashSet set = new HashSet(result);

    说明:迫切连接会使延迟加载失效;左外连接、右外连接以及内连接的查询结果可能会包含重复对象;左右外连接情况下主表有记录,次表没有记录的时候次表返回的对象为null。内连接情况下,只要有一方的表中没有记录,相应的另一方也不会出现在查询结果中。也就是内连接查询出的两个关联的对象都不为null,如果有一方为null,这条记录就不会出现在查询结果中了。


五、Hql方式检索一条记录

    如果Hql的查询结果只有一条记录,调用Query的uniqueResult()方法,该方法返回一个Object对象。如果查询结果包含多条记录,但是只需要抽取其中的一条使用,可以在调用uniqueResult()方法前先调用setMaxResult(1)方法限定只返回一条记录。


六、集合过滤

    一对多关系中多的一方通常是保存在set中,不利于排序、筛选、分页等需求。可以使用hibernate的集合过滤功能解决此问题。

List result = session.createFilter(customer.getOrders(),"where this.price>100 order by this.price").list();
    Session的createFilter()方法用来过滤集合,它具有以下特点:

①返回Query类型的实例。

②它的第一个参数指定一个持久化对象的集合。这个集合是否已经初始化没有关系,但是它所属的对象必须是持久化对象,也就是上述代码中customer对象必须是持久化对象。

③它的第二个参数指定过滤条件,它由合法的HQL查询语句组成。

④无论持久化对象的集合是否已经被初始化,Query的list()方法都会执行SQL查询语句,到数据库中检索Order对象。如果orders集合已经初始化了,Query的list()方法不会新建Order对象,仅返回已存在的Order对象的引用。如果orders集合没有初始化,Query的list()方法会创建相应的Order对象返回对它们的引用,但是不会初始化Customer对象的orders集合。


七、处理并发问题

    悲观锁的实现方式:

    1、在应用程序中显式指定采用数据库系统的独占锁来锁定数据资源。

select... for update
    Hibernate中使用get()和load()方法加载对象可以采用如下方式声明使用悲观锁:

Account a = (Account) session.get(Account.class,new Long(1),LockMode.UPGRADE);
    2、在数据库的ACCOUNTS(对应Account类)表中增加lock(锁)字段,可以是boolean类型,“true”表示锁定状态,“false”表示空闲状态。当一个事务先查询ACCOUNTS表中的ID为1的记录,然后再修改这条记录的时候,按以下步骤执行:

    (1)根据lock字段判断这条记录是否处于空闲状态。

    (2)如果处于锁定状态就一直等待,直到这条记录变成空闲状态。如果需要也可以撤销事务,抛出异常,通知客户系统正忙,稍后再执行该事务。

    (3)如果记录处于空闲状态,就把lock字段改为“true”,锁定这条记录。

    (4)更新这条记录,并把lock字段改为“false”,解除锁定。


内容概要:文章以“智能网页数据标注工具”为例,深入探讨了谷歌浏览器扩展在毕业设计中的实战应用。通过开发具备实体识别、情感分类等功能的浏览器扩展,学生能够融合前端开发、自然语言处理(NLP)、本地存储与模型推理等技术,实现高效的网页数据标注系统。文中详细解析了扩展的技术架构,涵盖Manifest V3配置、内容脚本与Service Worker协作、TensorFlow.js模型在浏览器端的轻量化部署与推理流程,并提供了核心代码实现,包括文本选择、标注工具栏动态生成、高亮显示及模型预测功能。同时展望了多模态标注、主动学习与边缘计算协同等未来发展方向。; 适合人群:具备前端开发基础、熟悉JavaScript和浏览器机制,有一定AI模型应用经验的计算机相关专业本科生或研究生,尤其适合将浏览器扩展与人工智能结合进行毕业设计的学生。; 使用场景及目标:①掌握浏览器扩展开发全流程,理解内容脚本、Service Worker与弹出页的通信机制;②实现在浏览器端运行轻量级AI模型(如NER、情感分析)的技术方案;③构建可用于真实场景的数据标注工具,提升标注效率并探索主动学习、协同标注等智能化功能。; 阅读建议:建议结合代码实例搭建开发环境,逐步实现标注功能并集成本地模型推理。重点关注模型轻量化、内存管理与DOM操作的稳定性,在实践中理解浏览器扩展的安全机制与性能优化策略。
基于Gin+GORM+Casbin+Vue.js的权限管理系统是一个采用前后端分离架构的企业级权限管理解决方案,专为软件工程和计算机科学专业的毕业设计项目开发。该系统基于Go语言构建后端服务,结合Vue.js前端框架,实现了完整的权限控制和管理功能,适用于各类需要精细化权限管理的应用场景。 系统后端采用Gin作为Web框架,提供高性能的HTTP服务;使用GORM作为ORM框架,简化数据库操作;集成Casbin实现灵活的权限控制模型。前端基于vue-element-admin模板开发,提供现代化的用户界面和交互体验。系统采用分层架构和模块化设计,确保代码的可维护性和可扩展性。 主要功能包括用户管理、角色管理、权限管理、菜单管理、操作日志等核心模块。用户管理模块支持用户信息的增删改查和状态管理;角色管理模块允许定义不同角色并分配相应权限;权限管理模块基于Casbin实现细粒度的访问控制;菜单管理模块动态生成前端导航菜单;操作日志模块记录系统关键操作,便于审计和追踪。 技术栈方面,后端使用Go语言开发,结合Gin、GORM、Casbin等成熟框架;前端使用Vue.js、Element UI等现代前端技术;数据库支持MySQL、PostgreSQL等主流关系型数据库;采用RESTful API设计规范,确保前后端通信的标准化。系统还应用了单例模式、工厂模式、依赖注入等设计模式,提升代码质量和可测试性。 该权限管理系统适用于企业管理系统、内部办公平台、多租户SaaS应用等需要复杂权限控制的场景。作为毕业设计项目,它提供了完整的源码和论文文档,帮助学生深入理解前后端分离架构、权限控制原理、现代Web开发技术等关键知识点。系统设计规范,代码结构清晰,注释完整,非常适合作为计算机相关专业的毕业设计参考或实际项目开发的基础框架。 资源包含完整的系统源码、数据库设计文档、部署说明和毕
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值