关于hibernate:Java持久化框架Hibernate与Mybatis

Java长久化框架:Hibernate与MybatisHibernate和Mybatis是两个Java长久化框架,用于将Java对象映射到关系型数据库中。在开发Web应用程序时,Java开发人员常常应用这些框架来解决数据长久化的事物。这篇文章将具体介绍Hibernate和Mybatis的特点、优劣势以及如何抉择哪个框架适宜您的我的项目。 1. HibernateHibernate是一个开源的对象关系映射框架,它提供了一种主动将Java对象映射到数据库表中的办法。与传统的JDBC相比,Hibernate大大简化了数据库操作,并且使得代码更加易于保护和了解。 特点 Hibernate实现了JPA(Java Persistence API)标准,因而它能够与任何实现JPA标准的ORM框架无缝集成。Hibernate应用了Hibernate Query Language(HQL),它是一种灵便的查询语言,容许您以面向对象的形式查询数据库。Hibernate反对多种数据库,包含MySQL、Oracle、PostgreSQL等,并且能够轻松地切换数据库。Hibernate还提供了二级缓存和查问缓存,这能够大大提高应用程序的性能。劣势 易于应用:Hibernate提供了非常简单的API,开发人员不须要编写简单的SQL语句来执行CRUD操作。易于保护:Hibernate使得代码更加易于保护和了解,大大降低了开发人员的工作量。易于扩大:Hibernate提供了丰盛的API,能够轻松扩大应用程序。劣势 性能:Hibernate的性能比Mybatis略差。在解决大量数据时,可能会呈现性能问题。然而,应用缓存和优化查问能够进步性能。复杂性:Hibernate的复杂性比Mybatis更高。初学者须要破费一些工夫学习框架的基本概念和操作。2. MybatisMybatis是另一个Java长久化框架,它通过XML或正文来映射Java对象和数据库记录。与Hibernate相比,Mybatis更重视SQL管制,并提供了更好的灵活性和可定制性。 特点 灵活性:Mybatis容许您齐全管制SQL查问过程,包含手写SQL查问语句、参数映射和后果映射。这使得Mybatis非常灵活。可定制性:Mybatis容许您自定义类型转换器、插件等,以满足非凡需要。易于学习:Mybatis的学习曲线比Hibernate更加平滑。初学者能够很快上手并开始编写查问。劣势 性能:Mybatis比Hibernate更快。它通过手写SQL语句和提供缓存来进步性能,特地是在解决大量数据时。灵活性:Mybatis非常灵活,容许您齐全管制SQL查问过程,并提供了很好的扩展性。劣势 复杂性:Mybatis须要开发人员编写简单的SQL语句,这对于初学者来说可能有些艰难。维护性:因为开发人员须要手写SQL语句,因而代码可能会变得简短和难以保护。3. 如何抉择在抉择Hibernate或Mybatis时,须要依据我的项目的理论需要进行评估。如果您的我的项目须要高度的可定制性和灵活性,并且解决大量数据,那么Mybatis可能更适宜您。如果您须要疾速地开发应用程序并放弃较低的保护老本,则Hibernate可能更适宜您。总之,两个框架各有优缺点,须要依据您的需要做出抉择。 论断Hibernate和Mybatis都是十分有用的Java ORM框架,能够帮忙开发人员更轻松地治理数据库操作。在抉择哪个框架时,请思考我的项目的理论需要,并依据其长处和毛病做出抉择。无论您抉择哪个框架,都须要破费一些工夫学习它的基本概念和操作,以便正确应用它们来开发高质量的应用程序。

May 21, 2023 · 1 min · jiezi

关于hibernate:jpa之hibernate和jackson踩坑记录

在做的我的项目采纳的是spring jpa,底层默认应用的是orm是hibernate,通过hibernate查问进去的实体对象实际上都是代理对象,在序列化的时候,咱们可能会遇到懒加载导致jackson无奈正确解析对象的问题,这个能够通过导入maven包 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId></dependency>而后加载进objectMapper上下文 @Beanpublic Jackson2ObjectMapperBuilderCustomizer builderCustomizer() { return builder -> { //jackson序列化 和 hibernate懒加载 builder.modulesToInstall(hibernate5Module()); };}private static Hibernate5Module hibernate5Module() { Hibernate5Module hibernate5Module = new Hibernate5Module(); hibernate5Module.configure(REPLACE_PERSISTENT_COLLECTIONS, true); //hibernate5Module.configure(FORCE_LAZY_LOADING, true); return hibernate5Module;}这样在序列化的时候就会判断之前是否存在数据了,如果是懒加载则会序列化为null。 我在反序列化的时候会报failed to lazily initialize a collection, could not initialize proxy...,起初看了一下序列化后的json,发现有些提早加载的Collection类型的会解析成org.hibernate.collection.internal.PersistentBag(hibernate的代理对象),而不是咱们个别json的汇合模式。导致反序列化的时候,会生成PersistentBag对象,这时候jackson操作PersistentBag,会导致hibernate检测到数据变动,从而触发它的一些操作而报下面的错。 而后我就排查,为啥序列化的时候会转成PersistentBag对象呢?能够看看Hibernate5Module里的代码,该模块会加载两个 context.addSerializers(new HibernateSerializers(_mapping, _moduleFeatures));context.addBeanSerializerModifier(new HibernateSerializerModifier(_moduleFeatures, _sessionFactory));//序列化处理器,咱们之前的解决懒加载原理也是通过这两个解决的,而后我在HibernateSerializerModifier里看到了PersistentCollectionSerializer,对于汇合类型的解析解决,其中次要有个判断办法protected boolean usesLazyLoading(BeanProperty property) { if (property != null) { // As per [Issue#36] ElementCollection ec = property.getAnnotation(ElementCollection.class); if (ec != null) { return (ec.fetch() == FetchType.LAZY); } OneToMany ann1 = property.getAnnotation(OneToMany.class); if (ann1 != null) { return (ann1.fetch() == FetchType.LAZY); } OneToOne ann2 = property.getAnnotation(OneToOne.class); if (ann2 != null) { return (ann2.fetch() == FetchType.LAZY); } ManyToOne ann3 = property.getAnnotation(ManyToOne.class); if (ann3 != null) { return (ann3.fetch() == FetchType.LAZY); } ManyToMany ann4 = property.getAnnotation(ManyToMany.class); if (ann4 != null) { return (ann4.fetch() == FetchType.LAZY); } // As per [Issue#53] return !Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER.enabledIn(_features); } return false;}发现我关联字段上应用的是@ManyToMany(fetch = FetchType.EAGER),因为我之前为了解决懒加载就加上的,当初能够去掉了,问题也失去了解决。 ...

November 13, 2021 · 1 min · jiezi

关于hibernate:jpa之hibernate和jackson踩坑记录

在做的我的项目采纳的是spring jpa,底层默认应用的是orm是hibernate,通过hibernate查问进去的实体对象实际上都是代理对象,在序列化的时候,咱们可能会遇到懒加载导致jackson无奈正确解析对象的问题,这个能够通过导入maven包 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId></dependency>而后加载进objectMapper上下文 @Beanpublic Jackson2ObjectMapperBuilderCustomizer builderCustomizer() { return builder -> { //jackson序列化 和 hibernate懒加载 builder.modulesToInstall(hibernate5Module()); };}private static Hibernate5Module hibernate5Module() { Hibernate5Module hibernate5Module = new Hibernate5Module(); hibernate5Module.configure(REPLACE_PERSISTENT_COLLECTIONS, true); //hibernate5Module.configure(FORCE_LAZY_LOADING, true); return hibernate5Module;}这样在序列化的时候就会判断之前是否存在数据了,如果是懒加载则会序列化为null。 我在反序列化的时候会报failed to lazily initialize a collection, could not initialize proxy...,起初看了一下序列化后的json,发现有些提早加载的Collection类型的会解析成org.hibernate.collection.internal.PersistentBag(hibernate的代理对象),而不是咱们个别json的汇合模式。导致反序列化的时候,会生成PersistentBag对象,这时候jackson操作PersistentBag,会导致hibernate检测到数据变动,从而触发它的一些操作而报下面的错。 而后我就排查,为啥序列化的时候会转成PersistentBag对象呢?能够看看Hibernate5Module里的代码,该模块会加载两个 context.addSerializers(new HibernateSerializers(_mapping, _moduleFeatures));context.addBeanSerializerModifier(new HibernateSerializerModifier(_moduleFeatures, _sessionFactory));//序列化处理器,咱们之前的解决懒加载原理也是通过这两个解决的,而后我在HibernateSerializerModifier里看到了PersistentCollectionSerializer,对于汇合类型的解析解决,其中次要有个判断办法protected boolean usesLazyLoading(BeanProperty property) { if (property != null) { // As per [Issue#36] ElementCollection ec = property.getAnnotation(ElementCollection.class); if (ec != null) { return (ec.fetch() == FetchType.LAZY); } OneToMany ann1 = property.getAnnotation(OneToMany.class); if (ann1 != null) { return (ann1.fetch() == FetchType.LAZY); } OneToOne ann2 = property.getAnnotation(OneToOne.class); if (ann2 != null) { return (ann2.fetch() == FetchType.LAZY); } ManyToOne ann3 = property.getAnnotation(ManyToOne.class); if (ann3 != null) { return (ann3.fetch() == FetchType.LAZY); } ManyToMany ann4 = property.getAnnotation(ManyToMany.class); if (ann4 != null) { return (ann4.fetch() == FetchType.LAZY); } // As per [Issue#53] return !Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER.enabledIn(_features); } return false;}发现我关联字段上应用的是@ManyToMany(fetch = FetchType.EAGER),因为我之前为了解决懒加载就加上的,当初能够去掉了,问题也失去了解决。 ...

November 13, 2021 · 1 min · jiezi

关于hibernate:java开发框架之Hibernate入门学习

Hibernate框架Hibernate框架简化了java应用程序与数据库交互的开发。 Hibernate是一个开源,轻量级的ORM(对象关系映射)工具。ORM工具简化了数据创立,数据处理和数据拜访。它是将对象java培训映射到数据库中存储的数据(表)的编程技术。ORM工具外部应用JDBC API与数据库进行交互。Hibernate框架的优缺点Hibernate框架有很多长处:开源和轻量级: Hibernate框架是依据LGPL许可证和轻量级的开源工具。疾速性能: Hibernate框架的性能很快,因为缓存在Hibernate框架外部应用。 hibernate框架中有两种类型的缓存:一级缓存和二级缓存。一级缓存默认是启用的。数据库独立查问: HQL(Hibernate查询语言)是面向对象的SQL版本。 它生成数据库独立查问。 所以你不须要编写数据库特定的查问语句。 在Hibernate之前,如果我的项目更改了数据库,咱们须要更改SQL查问,从而导致保护变得非常复杂。主动创立表: Hibernate框架提供了主动创立数据库表的性能。 因而,无需手动在数据库中创立表。简化简单连贯: 在hibernate框架中可轻松获取多个表中的数据。提供查问统计和数据库状态: Hibernate反对查问缓存,并提供无关查问和数据库状态的统计信息。毛病:不适宜须要应用数据库的特定优化机制的状况不适宜大规模的批量数据处理与MyBatis的比拟绝对于MyBatis的“SQL-Mapping”的ORM实现,Hibernate的ORM实现更加欠缺,提供了对象状态治理、级联操作等性能齐全面向对象,语句与数据库无关,开发者无需关注SQL的生成,开发简略,便于批改,数据库移植性好因为间接应用SQL,MyBatis应用自由度较高应用Hibernate步骤应用Hibernate APIHibernate的三大状态小结一下

October 28, 2021 · 1 min · jiezi

关于hibernate:JPA开发利器fastjpa使用介绍

介绍一款JPA开发利器fastjpa,它提供了对SpringBoot框架中对于对JPA的操作的二次封装 ,提供了面向对象的形式来操作JPQL/HQL,旨在缩小sql语句编写,疾速进步开发效率,使代码书写显的更加优雅和减少可读性 工具个性:面向对象形式的更新、删除和查问操作查问指定列名和函数列分组查问和过滤列表查问和过滤表连贯查问和过滤反对子查问分页查问和过滤装置<dependency> <groupId>com.github.paganini2008.springworld</groupId> <artifactId>fastjpa-spring-boot-starter</artifactId> <version>2.0.2</version></dependency>fastjpa-spring-boot-starter 依赖spring-boot-starter-data-jpa, 本质上是对JPA Criteria查问API(QBC)的再封装,并设计成流式格调的API(有点相似python的orm框架sqlalchemy) ,使得JPA面向对象查问的API不再难用 fastjpa 外围接口:EntityDaoModelJpaQueryJpaPageFilterColumnFieldJpaGroupByJpaSortJpaPageResultSetJpaQueryResultSetJpaUpdateJpaDelete大家有趣味的话,能够钻研其源码上面通过几个示例来演示一下fastjpa的几个外围接口的用法比方,当初有3个实体,用户,订单,商品 用户实体@Getter@Setter@Entity@Table(name = "demo_user")public class User { @Id @Column(name = "id", nullable = false, unique = true) private Long id; @Column(name = "name", nullable = false, length = 45) private String name; @Column(name = "phone", nullable = false, length = 45) private String phone; @Column(name = "vip", nullable = true) @org.hibernate.annotations.Type(type = "yes_no") private Boolean vip;}商品实体@Getter@Setter@Entity@Table(name = "demo_product")public class Product { @Id @Column(name = "id", nullable = false, unique = true) private Long id; @Column(name = "name", nullable = false, length = 45) private String name; @Column(name = "price", nullable = false, precision = 11, scale = 2) private BigDecimal price; @Column(name = "origin", nullable = true, length = 225) private String origin;}订单实体@Getter@Setter@Entity@Table(name = "demo_order")public class Order { @Id @Column(name = "id", nullable = false, unique = true) private Long id; @Column(name = "discount", nullable = true) private Float discount; @Column(name = "price", nullable = false, precision = 11, scale = 2) private BigDecimal price; @OneToOne(targetEntity = Product.class) @JoinColumn(nullable = false, name = "product_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) private Product product; @ManyToOne(targetEntity = User.class) @JoinColumn(nullable = false, name = "user_id", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT)) private User user; @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_time", columnDefinition = "timestamp null ") private Date createTime;}而后定义对应的Dao,须要继承fastjpa提供的EntityDao,但如果你不想应用fastjpa, 间接继承JpaRepositoryImplementation就行了 ...

August 8, 2021 · 8 min · jiezi

关于hibernate:Hibernate的升级Query用法

一、基本概念1、Configuration:     概述: Configuration类负责管理Hibernate的配置信息。启动Hibernate、创立SessionFactory对象。       (1)  Hibernate运行的底层配置信息:数据库的URL、用户名、明码、JDBC驱动类,数据库Dialect,数据库连接池等。       (2)  Hibernate对象关系映射文件(*.hbm.xml) 。    Hibernate配置的两种办法:      (1)属性文件( (hibernate.properties)                  调用代码:Configuration cfg = new Configuration();     (2)XML文件(hibernate.cfg.xml) 。                 调用代码:Configuration cfg = new Configuration().configure();                 configrure()办法默认读hibernate.cfg.xml 2.SessionFactory:    概述:应用程序从SessionFactory(会话工厂)里取得Session(会话)实例。它在多个利用线程间进行共享。       (1) SessionFactory是线程平安的(Thread-Safe),能够让多个执行线程同时存取SessionFactory而不会有数据共享的问题。      (2) 会话工厂缓存了生成的SQL语句和Hibernate在运行时应用的映射元数据。      (3) 须要留神的是SessionFactory是重量级的,因为个别状况下,一个我的项目通常只须要一个SessionFactory就够(单例模式),当须要操作多个数据库时,能够为每个数据库指定一个SessionFactory。   调用代码: SessionFactory sessionFactory =cfg.buildSessionFactory(); ...

June 14, 2021 · 3 min · jiezi

Hibernate-5-升级后-getProperties-错误

升级到 Hibernate 5 后,提示有错误: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Map;完整的错误栈为: java.lang.NoSuchMethodError: org.hibernate.engine.spi.SessionFactoryImplementor.getProperties()Ljava/util/Map;at org.hibernate.cache.internal.EnabledCaching.<init>(EnabledCaching.java:77)at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:33)at org.hibernate.engine.spi.CacheInitiator.initiateService(CacheInitiator.java:24)at org.hibernate.service.spi.SessionFactoryServiceInitiator.initiateService(SessionFactoryServiceInitiator.java:30)at org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.initiateService(SessionFactoryServiceRegistryImpl.java:68)at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)at org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.getService(SessionFactoryServiceRegistryImpl.java:109)at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:239)at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)如果你仅仅从错误信息,你可能看不出什么问题。 Google 搜索也找不到什么好的回答。经过我们对源代码的分析和排查,我们发现这是一个 JPA 版本不兼容的问题。 你的Hibernate 可能需要更高版本的 JPA,但是你的项目中只有低版本的 JPA 有关 JPA 的支持列表可以访问页面:https://hibernate.org/orm/releases/ 请查看 JPA 的版本配置列表。 根据我们的项目情况,我们需要使用 JPA 2.2,但是我们没有在 Maven 中指定 JPA 导致了上面的错误。

October 16, 2019 · 1 min · jiezi

java-bean-属性验证框架-valid

项目介绍java 开发中,参数校验是非常常见的需求。 但是 hibernate-validator 在使用过程中,依然会存在一些问题。 特性支持 fluent-validation支持 jsr-303 注解支持 i18n支持用户自定义策略支持用户自定义注解开源地址valid创作目的hibernate-validator 无法满足的场景如今 java 最流行的 hibernate-validator 框架,但是有些场景是无法满足的。 比如: 验证新密码和确认密码是否相同。(同一对象下的不同属性之间关系)当一个属性值满足某个条件时,才进行其他值的参数校验。多个属性值,至少有一个不能为 null其实,在对于多个字段的关联关系处理时,hibernate-validator 就会比较弱。 本项目结合原有的优点,进行这一点的功能强化。 validation-api 过于复杂validation-api 提供了丰富的特性定义,也同时带来了一个问题。 实现起来,特别复杂。 然而我们实际使用中,常常不需要这么复杂的实现。 valid-api 提供了一套简化很多的 api,便于用户自行实现。 自定义缺乏灵活性hibernate-validator 在使用中,自定义约束实现是基于注解的,针对单个属性校验不够灵活。 本项目中,将属性校验约束和注解约束区分开,便于复用和拓展。 过程式编程 vs 注解式编程hibernate-validator 核心支持的是注解式编程,基于 bean 的校验。 一个问题是针对属性校验不灵活,有时候针对 bean 的校验,还是要自己写判断。 本项目支持 fluent-api 进行过程式编程,同时支持注解式编程。 尽可能兼顾灵活性与便利性。 项目模块说明模块名称说明valid-api核心 api 及注解定义valid-core针对 valid-api 的核心实现valid-jsr针对 JSR-303 标准注解的实现valid-test测试代码模块依赖说明valid-core 默认引入 valid-api valid-jsr 默认引入 valid-core 快速开始准备工作JDK1.7+ Maven 3.X+ maven 引入<dependency> <groupId>com.github.houbb</groupId> <artifactId>valid-jsr</artifactId> <version>0.1.2</version></dependency>例子我们直接利用 jsr 内置的约束类: ...

October 13, 2019 · 6 min · jiezi

Springboot整合Hibernate拦截器时无法向拦截器注入Bean

开发环境JDK 1.8Springboot 2.1.1.RELEASEpom配置 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>关键代码实体类@Entitypublic class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}Repositorypublic interface UserRepository extends JpaRepository<User,Integer> {}自定义服务@Servicepublic class MyService { public void print(){ System.out.println(this.getClass().getSimpleName()+" call"); }}拦截器public class SimpleInterceptor extends EmptyInterceptor { @Resource private MyService myService; @Override public String onPrepareStatement(String sql) { myService.print(); System.out.println("sql:"+sql); return super.onPrepareStatement(sql); }}启动类@SpringBootApplicationpublic class BootHibernateInterceptorProblemApplication { public static void main(String[] args) { SpringApplication.run(BootHibernateInterceptorProblemApplication.class, args); }}配置## DataSourcespring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf8&useSSL=truespring.datasource.username=rootspring.datasource.password=123456789## hibernatespring.jpa.hibernate.ddl-auto=update## add interceptorspring.jpa.properties.hibernate.ejb.interceptor=com.rjh.interceptor.SimpleInterceptor单元测试类@RunWith(SpringRunner.class)@SpringBootTestpublic class BootHibernateInterceptorProblemApplicationTests { @Resource private UserRepository userRepository; @Test public void contextLoads() { System.out.println(userRepository.findAll()); }}运行结果java.lang.NullPointerException at com.rjh.interceptor.SimpleInterceptor.onPrepareStatement(SimpleInterceptor.java:20) ... ... ...分析根据异常信息,猜测是注入MyService失败 ...

June 28, 2019 · 2 min · jiezi

鱼与熊掌得兼Hibernate与Mybatis共存

鱼与熊掌得兼:Hibernate与Mybatis共存很长一段时间,网上有很多关于Hibernate与Mybatis孰优孰劣的争论,两个阵营的人谁也不能说服谁,每个人的理由都很有道理。今天,我分享的主题是:在一个项目中同时使用Hibernate和Mybatis两个ORM框架。 作为一个开发者,没有必要花费过多的时间去证明技术无用论,当你开始指责某个框架垃圾,另外一个框架最好时,隐性的暴露出你对某个框架没有深入的研究,无知的指责对于技术的提升没有任何的帮助。框架本身没有对错一说,只有适合和更适合项目的选择。任何框架都有自身的能力范围,就拿Hibernate和Mybatis这两个ORM框架来说,Hibernate封装了很多有用的API给开发者,降低了操作数据库的难度和复杂度,同时也减少了模板代码的数量,但Hibernate留给开发者可操作的空间相对Mybatis少了很多;Mybatis框架使用起来很灵活,开发者可以自定义查询语句,但增加了模板代码的数量,看起来没有Hibernate那么便捷。两种框架在便捷与灵活两个指标上做出了取舍与妥协,这不能说是框架的错。对于一个框架而言,需要有自身专注的领域和设计愿景,不可能面面俱到。 使用任何一种技术框架,都需要贴合现实的业务需求以及自身的技术能力。当你还没有深入的去了解一门技术或者当前业务需求无法与框架契合时,不要盲目的批判框架的好坏。今天,我不再去对比Hibernate与Mybatis两者之间的优劣,而是给出一个比较中庸的放方案,将两个ORM框架同时整合在一个项目中。 一、准备开发环境 如果你想成功运行本文中的源代码,需要满足一下的几个条件: 1、JDK : JDK 1.8.x及以上版本2、Maven : Maven 3.x或更高版本3、Git:版本控制工具,选择一个你喜欢的4、IDE : 选择你比较喜欢的一个代码编辑器,如STS、IntelliJ IDEA。笔者使用的是IntelliJ IDEA5、Databases : 选择一个你熟练使用的数据库系统。笔者在本文中使用的是MySQL 5.1.x版本的数据库系统如需获取本次分享内容的源代码进调试,可以到文章末尾找到源代码仓库连接 二、搭建项目2-1、引入依赖 为了快速构建项目,笔者采用Spring Boot来构建项目,同时使用加入Spring Data JPA和Mybatis两个ORM框架的依赖包。在此需要特别说明,Hibernate是一个JPA标准的实现,尔Spring Data JPA是一个JPA数据访问抽象,通过Spring Data JPA,可以轻松使用Hibernate框架。 你可以通过Spring Initializer来初始化项目,也可以通过IDEA自带的Spring Initializer功能构建项目,项目构建完成之后,pom.xml文件中的配置如下(包含但不限于文中给出的依赖项): 2-2、定义实体类-User.java 为了演示同时使用Hibernate和Mybatis操作数据库,需要提供一个实体类User.java,代码如下所示: 说明:在本次演示的项目中,使用到了Lombok插件,它可以让开发者减少模板代码的书写,提高开发速度。@Data注解可以自动生成类属性的getter、setter和toString方法。@NoArgsConstructor会自动为类生成无参构造函数,@AllArgsConstructor则会生成带全部属性的构造函数。 2-3、定义数据持久化接口 在本次课程中,将使用Spring Data JPA来完成写操作,如新增、修改、删除;使用Mybatis来完成读操作,如根据用户ID查询、查询所有的用户等。Spring Data JPA和MyBatis的持久化接口都位于com.ramostear.hm.orm包下,Spring Data JPA的持久化接口相对比较简单,之间继承JpaRepository类即可,代码如下: 说明:因为JPA只负责写操作,所以直接继承并使用JpaRepository提供的API即可,不需要额外的定义其他的接口方法。 下面是Mybatis的映射接口,定义了两个方法:根据ID查询用户信息和查询所有的用户信息。代码如下所示: 说明:此接口需要注意的地方是@Component和@Mapper注解,@Component注解标注此接口后,Spring会自动扫描并配置此类;@Mapper注解是把这个mapper的DAO交由Spring进行管理。 定义完Mybatis 映射接口后,需要提供一个进行数据库查询的xml配置文件。该文件位于resources/mapper文件夹中,UserMapper.xml完整代码如下: 2-4、定义UserService 在UserService接口中,提供三个方法:保存用户信息、根据ID查询用户、查询所有的用户。UserService接口代码如下: 在UserService接口的实现类中,需要同时注入UserRepository和UserMapper两个依赖。我们使用构造函数的方式来注入这两个依赖。代码如下: 说明:@Transactional注解用于设置每个方法的事务控制方式。@Service注解声明该类是一个服务提供类,且设置了该类被Spring初始化时Bean对象的名称为“userService”。 2-5、定义控制器 最后,提供一个控制器,用于处理客户端的相关请求。在控制器中,提供了三个请求处理方法,分别处理客户端新增用户、根据ID查询用户和查询所有用户的请求。控制器代码如下: 说明:在本次教程中,为了编码IDEA报警告,所有的依赖注入都采用构造函数的方式注入相关的依赖。 三、配置Hibernate和Mybatis 网络上有很多关于在Spring Boot项目中配置Hibernate和Mybatis的教程,但同时配置Hibernate和Mybatis的文章很少,有一些是通过Java代码的方式对这两个ORM框架进行配置,采用的是多数据源的方法来整合两个框架。其实整合这两个框架没有想象中的那么难,只需要在application.yml或者application.properties配置文件中加入几行代码,就可以完成两个框架的整合。以application.yml配置文件为例,配置代码如下: ...

June 14, 2019 · 1 min · jiezi

Springboot2快速集成minidao持久层

Springboot2 快速集成minidao持久层这里采用springboot版本号: 2.0.4.RELEASEminidao已经提供自定义starter,集成非常简单,直接pom引入minidao-spring-boot-starter依赖即可集成步骤: 第一步: pom引入starter依赖包<dependency> <groupId>org.jeecgframework</groupId> <artifactId>minidao-spring-boot-starter</artifactId> <version>1.6.7.RELEASE</version></dependency>此starterd对应的minidao最新版本 1.6.7 默认提供了mysql的依赖。 第二步:配置minidao的配置参数 (application.properties 或者 application.yml) minidao: base-package: com.springBoot.* db-type: mysql show-sql: true第三步: 配置springjdbc所需数据源 ( application.yml) spring: datasource: url: jdbc:mysql://localhost:3306/minidao-pe username: root password: root driver-class-name: com.mysql.jdbc.Driver通过以上三步,minidao集成完毕。 参考源码下载: https://download.csdn.net/dow...技术交流群:325978980 Minidao常见配置参数说明:参数名用途默认值base-packageminidao扫描路径*db-type数据库类型,常用配置: mysql/postgresql/oracle/sqlservermysqlshow-sql是否打印sqltrueempty-interceptorminidao拦截器的bean名字空keyType是使用什么字母做关键字Map的关键字 默认值origin 即和sql保持一致,lower小写(推荐),upper 大写origin

June 7, 2019 · 1 min · jiezi

hibernatevalidator版本问题引发的tomcat7下项目启动失败

线上环境jdk8tomcat7.0.90 报错java.lang.NoClassDefFoundError:javax/el/ELManager原因版本引入包:hibernate-validator:6.0.16.Final版本,使用邮箱验证注解功能,则需要依赖加载对应javax/el/ELManager。因线上使用tomcat版本为tomcat7.0.90,在其CATALINA_HOME/lib目录下的el-api.jar的版本为2.2版本,无类javax/el/ELManager。(tomcat8中,el-api.jar包升级为3.0版本,不会找不到类)因此启动失败。 解决将hibernate-validator版本降到5.4.3.Final版本,去除引用校验邮箱的注解特性,该用业务代码处理 为什么线下未发现呢?因为线下环境使用的tomcat版本为8.5.32版本,与线上未统一。当然这也是不合理的点

May 28, 2019 · 1 min · jiezi

Hibernate-Validator更简洁的参数校验及一个util

代码地址https://github.com/wheel-orga...简介hibernate-validator是Hibernate项目中的一个数据校验框架,是Bean Validation 的参考实现,hibernate-validator除了提供了JSR 303规范中所有内置constraint 的实现,还有一些附加的constraint。使用hibernate-validator能够将数据校验从业务代码中脱离出来,增加代码可读性,同时也让数据校验变得更加方便、简单。 官网地址:http://hibernate.org/validator/ 如何使用项目中已经引入了需要的api,无需重复引入<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version></dependency><dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.16.Final</version></dependency>在要校验的POJO上加上以下注解即可 注解用途Valid递归的对关联的对象进行校验AssertFalse用于boolean字段,该字段的值只能为falseAssertTrue用于boolean字段,该字段只能为trueDecimalMax(value)被注释的元素必须是一个数字,只能大于或等于该值DecimalMin(value)被注释的元素必须是一个数字,只能小于或等于该值Digits(integer,fraction)检查是否是一种数字的(整数,小数)的位数Future检查该字段的日期是否是属于将来的日期FutureOrPresent判断日期是否是将来或现在日期Past检查该字段的日期是在过去PastOrPresent判断日期是否是过去或现在日期Max(value)该字段的值只能小于或等于该值Min(value)该字段的值只能大于或等于该值Negative判断负数NegativeOrZero判断负数或0Positive判断正数PositiveOrZero判断正数或0NotNull不能为nullNull必须为 nullPattern(value)被注释的元素必须符合指定的正则表达式Size(max, min)检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等Length(max, min)判断字符串长度CreditCardNumber被注释的字符串必须通过Luhn校验算法,银行卡,信用卡等号码一般都用Luhn计算合法性Email被注释的元素必须是电子邮箱地址Length(min=, max=)被注释的字符串的大小必须在指定的范围内NotBlank只能用于字符串不为null,并且字符串trim()以后length要大于0NotEmpty集合对象的元素不为0,即集合不为空,也可以用于字符串不为nullRange(min=, max=)被注释的元素必须在合适的范围内SafeHtmlclasspath中要有jsoup包ScriptAssert要有Java Scripting API 即JSR 223("Scripting for the JavaTMPlatform")的实现URL(protocol=,host=,port=,regexp=,flags=)被注释的字符串必须是一个有效的url更多功能,如:自定义校验规则、分组校验、关联参数联合校验请查看官网或百度 Dubbo中使用Hibernate Validator校验入参无需util,Dubbo接口配置上的validation为true即可 在客户端验证参数 <dubbo:reference id="xxxService" interface="xxx.ValidationService" validation="true" />在服务器端验证参数 <dubbo:service interface="xxx.ValidationService" ref="xxxService" validation="true" />在代码里校验入参//obj为包含Hibernate Validator注解的POJO//快速失败模式ValidResult validResult = ValidationUtil.fastFailValidate(obj);//obj为包含Hibernate Validator注解的POJO//全部校验模式ValidResult validResult = ValidationUtil.allCheckValidate(obj);样例public class ParamTestDTO implements Serializable { private static final long serialVersionUID = 7123882542534668217L; @AssertTrue(message = "Error True") private Boolean testTrue; @AssertFalse(message = "Error False") private Boolean testFalse; @DecimalMax(value = "10", message = "Error StrMax") private String testStrMax; @DecimalMin(value = "1", message = "Error StrMin") private String testStrMin; @Max(value = 10, message = "Error Max") private Integer testMax; @Min(value = 1, message = "Error Min") private Double testMin; @Digits(integer = 2, fraction = 3, message = "Error Dig") private BigDecimal testDig; @Past(message = "Error Past") private Date testPast; @Future(message = "Error Future") private Date testFuture; @Null(message = "Error Null") private String testNull; @NotNull(message = "Error NonNull") private String testNonNull; @Pattern(regexp = "^[0-9]?[0-9]$", message = "Error Pattern") private String testPattern; @Size(min = 1, max = 10, message = "Error Size") private List<String> testSize; @Length(min = 1, max = 10, message = "Error Length") private String testLength; @NotBlank(message = "Error Blank") private String testBlank; @NotEmpty(message = "Error NotEmpty") private String testEmpty; @Range(min = 1, max = 10, message = "Error Range") private String testRange;}单测:ValidationUtilTest ...

May 13, 2019 · 2 min · jiezi

聊聊hibernate的sessionlevel-repeatable-reads

序本文主要研究一下hibernate的session-level repeatable reads 实例doInTransaction(session -> { Product product = new Product(); product.setId(1L); product.setQuantity(7L); session.persist(product);});doInTransaction(session -> { final Product product = (Product) session.get(Product.class, 1L); try { executeSync(() -> doInTransaction(_session -> { Product otherThreadProduct = (Product) _session.get(Product.class, 1L); assertNotSame(product, otherThreadProduct); otherThreadProduct.setQuantity(6L); })); Product reloadedProduct = (Product) session.createQuery("from Product").uniqueResult(); assertEquals(7L, reloadedProduct.getQuantity()); assertEquals(6L, ((Number) session .createSQLQuery("select quantity from product where id = :id") .setParameter("id", product.getId()) .uniqueResult()) .longValue() ); } catch (Exception e) { fail(e.getMessage()); }});这段代码展示了hibernate的session-level repeatable reads功能,这里reloadedProduct查询返回的是session中id为1的entity的缓存(但是也向db发出了sql语句,只是没有使用其返回的resultSet的值),而project操作查询则直接根据jdbc查询返回的结果返回实例代码来自How does Hibernate guarantee application-level repeatable readswrite-behindwrite-behind cachewrite-behind cache是cache策略的一种,其主要思路就是更新数据是首先更新cache,之后cache在批量持久化到存储中,比如批量更新到数据,这样做的好处是可以合并数据的多次操作减少IOtransactional write-behind cachehibernate为了减少数据库连接加锁的时间,设计了transactional write-behind的策略,其persistence context充当transactional write-behind cache的角色,对entity的改动都先作用到内存,等到一定时机在flush到数据库;具体体现在Session类中To reduce lock contention in the database, the physical database transaction needs to be as short as possible.Long-running database transactions prevent your application from scaling to a highly-concurrent load. Do not hold a database transaction open during end-user-level work, but open it after the end-user-level work is finished.This concept is referred to as transactional write-behind. ...

April 24, 2019 · 9 min · jiezi

Hibernate5.2-5.3版本的ManyToOne、OneToOne的延迟加载(fetch=Lazy)失效

项目使用springboot的1.5.19版本进行开发,里面使用到JPA,而springboot这个版本自带的JPA实现是Hibernate的5.0.12版本。这个版本里面的延迟加载是没有问题的,当你设置 fetch = FetchType.LAZY 时,关联的对象在你没有使用的时候,是不会发出sql的。但升级了springboot的2.1.3后,依赖的Hibernate已经去到5.3.7版本了。这时候延迟加载就失效了,就算只是查询一个字段,也会再发多一条关联对象的SQL出来。这个问题,在升级Hibernate5.4.2版本后是解决了的。而对于使用springboot 2.1.3的话,需要先排除掉spring自身加载的Hibernate版本,自己替换成Hibernate5.4.2版本才可以。不知道有没有人遇到这问题,本人测试,Hibernate5.2.x时候就已经出现了,但好像一直没有修复。

March 27, 2019 · 1 min · jiezi

hibernate多对多,单个修改很伤神

导读客户单击项目详细时,会跳转到项目详细页面。用户单击红框中的加号,页面弹出选择标签页面。用户单击待选标签中的标签,当前标签会被保存到数据库中。当然,项目标签与项目之间是多对多的关系,也就是说,一个项目可以有多个标签,一个标签对应多个项目,总体来说,它们之间是多对多的关系。多对多的关系多也是相对的多,就像导读中的项目和项目标签一样。这是从宏观上来说的多对多,但从微观上来看,其内部还是一对多的关系。一个项目可以有多个项目标签,一个项目标签可以对应多个项目。既然对各自来说,都是一对多的关系,为什么不这样写呢:// 项目表@Entity@Table(name = “zq_project”)public class Project extends BaseObj { /** * 項目标签 / @ManyToOne @JoinColumn(name = “attach_id”) private List<DataDict> tagss;}//项目标签表@Entity@Table(name = “core_data_dict”, uniqueConstraints = {@UniqueConstraint(columnNames = {“tenant_id”, “code”}, name = “uk_dd_tid_code”)})public class DataDict extends ToString{ /* * 項目 / @ManyToOne @JoinColumn(name = “attach_id”) private List<Project> projectList; }而是这样书写方式呢? /* * 添加项目标签,如果是标签来源于数据字典,则可以直接读取数据字典 / @ManyToMany(fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) @JoinTable( name = “zq_project_tags”, joinColumns = {@JoinColumn(name = “project_id”)}, inverseJoinColumns = @JoinColumn(name = “tag_code”) ) @JSONField(serialize = false) private List<DataDict> tagList;因为这样书写,hibernate会根据项目和数据字典(项目标签放在数据字典表中)生成一个中间表,如图所示:为什么不采用第一种方式呢?1、第一种方式扩展性不好,破坏框架结构,为什么这么说?项目标签放置在数据字典中,换句话,数据字典所存储的数据集不会经常改变。就像我们的新华字典,不会时常做版本更新。因而,类似于项目标签、数值型的单位呀、系统标量等,这些数据都不会时常改变。而且,现在的项目都采用maven构建,什么是maven项目呢?很多博客都有详细介绍,我只做简单地介绍。maven是项目对象模型(POM),举一个简单的例子,我们在不使用maven创建项目时,往往需要拷贝jar包到项目中。然而,我们使用maven创建项目时,就会创建一个pom文件。我们在pomp文件中做配置,如果需要新的jar包,我们直接使用<dependence>…</dependence> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency>当然,引用jar包是一回事,还有模块化构建项目。我们经常分模块构建项目,比如信息发布一个模块,核心层一个模块,自定义业务层一个模块。各个模块之间就像是Java一样,具有继承和关联关系。子pom文件可以使用父pom的所有jar包。如图所示:层级非常严格,即便在同一个项目不同的模块中,我们如果想要调用另外一个模块的类时,也需要用dependency来引用其模块。比如,我们想要在platform-custom层调用platform-core层的类,我们需要在platform-custom的pom文件中写入依赖: <dependency> <groupId>com.zfounder.platform</groupId> <artifactId>platform-core</artifactId> </dependency>但是,项目表是自定义业务范畴的,是在platform-custom模块中的,而数据字典是属于platform-core模块的。因为platform-core的pom没有依赖platform-custom模块,因而,数据字典无法调用platform-custom模块中的项目类,如图所示:因而,如果我们采用这种方式创建多对多的关系,会破坏maven结构或者框架结构。2、对于数据库的维护性不好。对于项目来说,相同项目标签的可能有数条项目的记录,但是,每个项目都是唯一的,一个项目应该有多个标签,但现在的问题是,同一个项目的编号不同了,因为不同的项目标签,这就破坏了数据库的唯一性。针对以上两种情况,我们应该使用中间表,来创建多对多的关系。如图所示:这样简洁明快,也便于日后的维护工作。哪些是多对多的关系1、用户和角色之间的关系,一个用户可以有多重角色,一个角色可以对应多个用户。2、既然说到了角色,自然说到角色所能管理的权限,比如当用户登录,根据用户的角色,需要展示哪些页面,可以操作该页面的哪些按钮,等等。这些都放在资源表中。如图所示:3、项目和图片的关系,一个项目有很多张图片,但是一张图片也可以被多个项目使用。4、。。。。中间表一般都涉及两个字段,字段分别引用两张表的主键。多对多的实例,以选择项目标签为例我们在点击保存项目标签时,心想我们点击一次,保存一次,而项目类中项目标签对象是一个集合对象,我们应该创建这个集合,然后拿到前端穿过来的项目标签的code值(数据字典对应的是code值),通过数据字典的事务方法获取这个数据字典对象,再判断该对象的父类是不是项目标签。因为数据字典存储很多类字典,比如项目标签字典、单位字典、系统变量字典等等。然后保存这个数据字典,但结果事与愿违,它会覆盖数据库中原来的数据,这是更新数据,而不是保存数据,如图所示:为什么会这样,因为,其底层使用的是merge方法,如代码所示: /* * @see com.zfounder.platform.core.dao.GenericDao#save(T) /@SuppressWarnings(“unchecked”)@Overridepublic T save(T object) { Session session = getSession(); return (T) session.merge(object);}merger如果发现数据库存在改编号的对象,则执行update操作;如果不存在,则执行insert操作。因而,中间表,即项目标签表,已经存在该项目编号的id,所以,执行更新操作。我们换一种思路,首先看项目类的部分源码:@Data@AllArgsConstructor@NoArgsConstructor@Entity@Table(name = “zq_project”)public class Project extends BaseObj { /* * 项目名称 / @Column(name = “name”) private String name; /* * 录入日期 / @Column(name = “sign_date”) private Date signDate; /* * 备注 / @Lob @Basic(fetch = FetchType.LAZY) @Column(name = “remark”, columnDefinition = “mediumtext”) private String remark; /* * 预算金额 / @Column(name = “budgetary_amount”, precision = 10, scale = 2) private BigDecimal budgetaryAmount; /* * 工程起始时间 / @Column(name = “start_time”) private Date startTime; /* * 工程结束时间 / @Column(name = “end_time”) private Date endTime; /* * 添加项目标签,如果是标签来源于数据字典,则可以直接读取数据字典 */ @ManyToMany(fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) @JoinTable( name = “zq_project_tags”, joinColumns = {@JoinColumn(name = “project_id”)}, inverseJoinColumns = @JoinColumn(name = “tag_code”) ) @JSONField(serialize = false) private List<DataDict> tagList;}因为项目标签时多对多的关系,当我们接收到前端传过来的项目编号,并调用项目的事务的get方法,创建当前项目瞬时态的对象时。hibernate根据项目标签表中的code值,创建项目标签(数据字典)的集合对象,并填充到tagList的集合中。我们只要在对集合对象上,添加从前端穿过的code值,并通过code值创建项目标签(数据字典)的对象即可。因而,代码改成下面的方式:这样保存就没问题了。结尾如果我们掌握了最基本知识是,就可以很容易掌握其他语言了。加油,致自己。 ...

March 11, 2019 · 1 min · jiezi

Query和SQLQuery的区别

@Overridepublic Goods checkGoods(Long categoryId, Long tenantId, String cardNo) { String sql = “SELECT " + " * " + “FROM " + " cce_goods " + “WHERE " + " is_deleted = 0 " + " AND goods_category_id = ‘” + categoryId + “’ " + " AND commercial_tenant_id =’” + tenantId + “’ " + " AND card_no = ‘” + cardNo + “’”; Query query =null; query=getSession().createSQLQuery(sql).addEntity(Goods.class); query.setParameter(“0”,1); query.setResultTransformer(Transformers.aliasToBean(Goods.class)); query=getSession().createQuery(sql); return (Goods) query.uniqueResult();}今天在做dao层操作时,一不小心写错了,得到两种情况,一种是 query=getSession().createSQLQuery(sql).addEntity(Goods.class);和query=getSession().createQuery(sql)。也是可以的,为什么能这样呢?查看hibernate底层源码时发现,SQLQuery继承Query接口,如图所示:接口的实现指向它的实现类,或者它子接口的实现类。我们需要调用addEntity(Goods.class)增加商品类的实体类,hibernate的底层通过反射将数据填充到商品类的实例化的对象中的。但是,接口Query本身是没有这个方法的,因而,我们需要使用SQLQuery,而不是Query。【备注】我是比较看源码的,多看看源码,还是有好处的。 ...

March 9, 2019 · 1 min · jiezi

Hibernate之CRUD与实例状态

在上一篇《初识Hibernate》中简单介绍了在Hibernate如何加载对象和持久化对象,以及Hibernate中对象实例状态。本文将继续介绍Hibernate简单的增删改查方法和对对象实例状态的理解(查询方法暂不提及)。一、HibernateのCRUD操作1.1 HibernateのCreate @Test public void testCreate() { Session session = HibernateUtils.getSession(); User user = new User(); user.setId(3); user.setUserName(“Sandy”); user.setPassWord(“aaa”); session.save(user); }注意:Hibernate事务默认是关闭的,执行后不会报错,但是数据并没有成功插入数据库。控制台输出:Schema update complete解决办法:手动设置事务提交 @Test public void testCreate() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user = new User(); user.setId(3); user.setUserName(“Sandy”); user.setPassWord(“aaa”); session.save(user); tx.commit(); }控制台输出:Hibernate: insert into tbl_user (username, password, id) values (?, ?, ?)save()方法把一个临时对象加入到Session缓存中,并持久化该临时对象,计划执行一个insert语句。1.2 HibernateのRead这里的查询准确来说应该称为对象加载。Hibernate中session.get()方式获取被称为对象加载,只能从数据库中加载出唯一一条记录。查询多条数据Hibernate另提供了API。 @Test public void testRead() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user = (User)session.get(User.class, 3); System.out.println(user); tx.commit(); }控制台输出:Hibernate: select user0_.id as id1_8_0_, user0_.username as username2_8_0_, user0_.password as password3_8_0_ from tbl_user user0_ where user0_.id=?User{id=3, userName=‘Sandy’, passWord=‘aaa’}get()和load() 试图从数据库加载一个实体对象时,Session先判断对象是否存在,如果存在就不到数据库中检索。返回的对象都位于Session缓存中,接下来修改了持久化对象的属性后,当Session清理缓存时,会根据持久化对象的属性变化来同步更新数据库。 区别: (1)当数据库中不存在与OID对应的记录时,load()方法抛出ObjectNotFoundException异常,而get()方法返回null. (2)两者采用不同的检索策略。 默认情况下,load()方法采用延迟检索策略(Hibernate不会执行select语句,仅返回实体类的代理类实例,占用内存很少);而get()采用立即检索策略(Hibernate会立即执行select语句)。 使用场合: (1)如果加载一个对象的目的是为了访问它的各个属性,可以用get(); (2)如果加载一个对象的目的是为了删除它,或者建立与别的对象的关联关系,可以用load() ;1.3 HibernateのUpdate更新操作并没有使用session.update()方法,直接tx.commit()便能够成功更新数据。session.upate()方法另有作用。 @Test public void testUpdate() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user = (User)session.get(User.class, 3); user.setUserName(“Bob”); user.setPassWord(“123”); // 脏数据(Dirty Data) // 过时数据检测(前提是该对象是持久态) tx.commit(); }控制台输出:Hibernate: select user0_.id as id1_8_0_, user0_.username as username2_8_0_, user0_.password as password3_8_0_ from tbl_user user0_ where user0_.id=?Hibernate: update tbl_user set username=?, password=? where id=?Session在清理缓存的时候会自动进行脏检查(dirty-check),如果发现Session缓存中的对象与数据库中相应的记录不一致,就会同步数据库。1.4 HibernateのDelete @Test public void testDelete() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user = (User)session.get(User.class, 3); user.setUserName(“Bob”); user.setPassWord(“123”); session.delete(user); tx.commit(); }控制台输出:Hibernate: select user0_.id as id1_8_0_, user0_.username as username2_8_0_, user0_.password as password3_8_0_ from tbl_user user0_ where user0_.id=?Hibernate: delete from tbl_user where id=?计划执行一个delete语句,把对象从Session缓存中删除。1.5 saveOrUpdate同时包含了save()和update()方法的功能。如果传入的是临时对象,就调用save()方法;如果传入的是游离对象,就调用update()方法如果传入的是持久化对象,就直接返回。 @Test public void testSaveOrUpdate() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user = new User(); user.setId(5); user.setUserName(“Sandy”); user.setPassWord(“123”); session.saveOrUpdate(user); User user2 = (User)session.get(User.class, 1); user2.setUserName(“Andy”); user2.setPassWord(“apple”); session.saveOrUpdate(user2); tx.commit(); session.close(); }控制台输出:Hibernate: select user_.id, user_.username as username2_8_, user_.password as password3_8_ from tbl_user user_ where user_.id=?Hibernate: select user0_.id as id1_8_0_, user0_.username as username2_8_0_, user0_.password as password3_8_0_ from tbl_user user0_ where user0_.id=?Hibernate: insert into tbl_user (username, password, id) values (?, ?, ?)Hibernate: update tbl_user set username=?, password=? where id=?update()方法把游离对象加入当前Session缓存中,计划执行update语句。当update()方法关联一个游离对象时,如果session缓存中已经有一个同类型且ID相同的持久化对象,那么update()方法会抛出NonUniqueException异常。当update()方法关联一个持久化对象时,该方法不起作用。close():清空session缓存。二、Hibernateの实例状态Hibernate中的对象有3中状态,瞬时对象(TransientObjects)、持久化对象(PersistentObjects)和离线对象(DetachedObjects也叫做脱管对象)。Java对象在Hibernate持久化层的状态:瞬时(transient)状态:刚用new语句创建,还没有被持久化,并且不处于session缓存中(处于临时状态的对象成为临时对象)。持久化(Persistent)状态:已经被持久化,并且加入到session缓存中。处于持久化状态的对象称为持久化对象。游离(Detached)状态:已经被持久化,但不再处于session缓存中。处于游离状态的对象称为游离对象。删除状态:不再处于session缓存中,并且session已经计划将其从数据库中删除。 @Test public void testState() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); // 持久态对象 // 1.在数据库中有唯一一条记录关联 2.必须跟一个session关联(必须纳入Hibernate容器) User user = (User)session.get(User.class, 1); // 临时态 // User user = new User(); user.setUserName(“Fancy”); user.setPassWord(“abc”); // 脏数据(Dirty Data)过时数据检测(前提是该对象是持久态) tx.commit(); // 关闭session session.close(); // 游离态(Detached) // 1.在数据库中有唯一一条记录对应 2.当前user没有跟Hibernate的session关联(曾经跟Hibernate关联,现在没有) // 无法进行脏数据检测 System.out.println(user); //把游离态对象变为持久态(再次纳入Hibernate容器管理,再跟一个session关联起来) Session session2 =HibernateUtils.getSession(); Transaction tx2 = session2.beginTransaction(); user.setUserName(“Kathy”); user.setPassWord(“good”); //把游离态对象同一个session关联,将对象状态变为持久态 session2.update(user); tx2.commit(); }}控制台输出:Hibernate: select user0_.id as id1_8_0_, user0_.username as username2_8_0_, user0_.password as password3_8_0_ from tbl_user user0_ where user0_.id=?User{id=1, userName=‘Fancy’, passWord=‘abc’}Hibernate: update tbl_user set username=?, password=? where id=? ...

March 2, 2019 · 2 min · jiezi

初识Hibernate

一、什么是Hibernate?Hibernate (开放源代码的对象关系映射框架): Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的ORM框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。<!– more –>二、为什么使用Hibernate?2.1 Hibernate相对于传统JDBC的优点对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作。hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。2.2 Hibernate相对于MyBatis的区别与联系两者区别:两者联系:缓存方面相同点:Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓 存方案,创建适配器来完全覆盖缓存行为。不同点:Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存。MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。三、Hibernate的使用准备好User实体类和数据库记录User.javapublic class User { private Integer id; private String userName; private String passWord; //Get、Set方法,此处省略。}mysql数据库:3.1 添加pom依赖Hibernate基础Maven依赖:<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!– 添加hibernate的依赖 开始–> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <!– http://mvnrepository.com/artifact/org.hibernate/hibernate –> <!– https://mvnrepository.com/artifact/org.hibernate/hibernate-core –> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.3.2.Final</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <!– <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.1</version> </dependency> &lt;!&ndash; Hibernate library dependecy end &ndash;&gt; <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency>–> <!– 添加hibernate的依赖 结束–> <!– mysql数据库的驱动包 –> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies>3.2 hibernate.cfg.xml配置文件<?xml version=“1.0” encoding=“utf-8”?><!DOCTYPE hibernate-configuration PUBLIC “-//Hibernate/Hibernate Configuration DTD 3.0//EN” “http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"><hibernate-configuration> <session-factory> <!–<property name=“hibernate.bytecode.use_reflection_optimizer”>false</property>–> <!–指定数据库的驱动程序–> <property name=“hibernate.connection.driver_class”>com.mysql.jdbc.Driver</property> <!–指定连接数据库的口令–> <property name=“hibernate.connection.password”>admin</property> <!–指定连接数据库的URL–> <property name=“hibernate.connection.url”>jdbc:mysql://localhost:3306/Hibernatetraning</property> <!–指定连接数据库的用户名–> <property name=“hibernate.connection.username”>root</property> <!–数据库方言,用于指定使用的sql从属于哪个数据库–> <property name=“hibernate.dialect”>org.hibernate.dialect.MySQLDialect</property> <!–如果为true,表示在程序运行时会在控制台输出SQL语句,有利于跟踪Hibernate的运行状态,默认为false。应用开发阶段设置为true,发布阶段应设置为false提高运行性能–> <property name=“show_sql”>true</property> <!– <property name=“hibernate.hbm2ddl.auto”>update</property>–> <mapping resource=“mapper/User.hbm.xml”></mapping> </session-factory></hibernate-configuration>3.2 User.hbm.xml映射文件<?xml version=“1.0” encoding=“utf-8”?><!DOCTYPE hibernate-mapping PUBLIC “-//Hibernate/Hibernate Mapping DTD 4.0//EN” “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping> <class name=“com.entity.User” table=“tbl_user”> <!– 在User里面 id 的set 和 get 方法里 也要设置为Integer类型的,不然会报错 –> <id name=“id” column=“id” type=“java.lang.Integer”> <generator class=“assigned”/> </id> <property name=“userName” column=“username” type=“java.lang.String” length=“20” /> <property name=“passWord” column=“password” type=“java.lang.String” length=“20”/> </class></hibernate-mapping>测试 Configuration config = new Configuration().configure(); SessionFactory sessionFactory = config.buildSessionFactory(); //测试session是否创建,以及是否可以从user映射的表里获取记录 Session session = sessionFactory.openSession(); //System.out.println(“session=="+session); User user = (User)session.get(User.class,1); System.out.println(user); //关闭会话工厂,项目自动停止。生产环境中千万不要写这句。 //sessionFactory.close();控制台输出:User{id=1, userName=‘Jack’, passWord=‘123’}四、Hibernate基本概念Hibernate是自动化程度更高ORM(Object-Relational Mapping)框架,不是面向SQL的持久层框架,以更加OO方式来编写数据读写代码。4.1 对象与关系型数据库注意:一定要清楚当前操作的东西,是属于对象层面的还是关系型数据库层面的,解决异常也是如此。Hibernate中的对象有3中状态,瞬时对象(Transient Objects)、持久化对象(Persistent Objects)和离线对象(Detached Objects也叫做脱管对象)。4.2 HibernateUtils.java工具类public class HibernateUtils { private static SessionFactory sessionFactory = null; static { try { Configuration configuration = new Configuration().configure(); ServiceRegistry serviceRegistry = new ServiceRegistryBuilder() .applySettings(configuration.getProperties()) .build(); sessionFactory = configuration.buildSessionFactory(serviceRegistry); }catch (HibernateException e){ System.out.println(“SessionFactory初始化失败”); e.printStackTrace(); } } public static SessionFactory getSessionFactory() throws Exception{ if (sessionFactory != null){ return sessionFactory; }else { throw new Exception(“sessionFactory为空,请检查配置文件”); } } public static Session getSession(){ Session session = null; try { if (sessionFactory != null){ session = sessionFactory.openSession(); } }catch (HibernateException e){ e.printStackTrace(); } return session; } public static void closeSession(Session session){ try { if (session != null && session.isOpen()){ session.close(); } }catch (HibernateException e){ e.printStackTrace(); } }}4.3 瞬时对象与持久化对象测试代码: Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); User user1 = new User(); user1.setId(2); user1.setUserName(“Frank”); user1.setPassWord(“111”); // 当前user1在数据库中没有记录进行关联,所有此时user1是瞬时对象。 // 将user1持久化 session.save(user1); tx.commit(); User user2 = (User)session.get(User.class, 2); System.out.println(user2); // 当前user2在数据库有唯一一条记录对应,所有此时user2是持久化对象。4.4 hibernate.hbm2ddl.autohibernate.hbm2ddl.auto属性值(从类自动生成数据库DDL操作)<property name=“hibernate.hbm2ddl.auto”>update</property>create:如果设置为该值,则每次加载hibernate时(准确说应是创建SessionFactory时)都会删除以前创建的表而根据model重新生成表,即使前后的表没有任何变化,通常会造成数据库数据丢失,需谨慎使用这个取值。create-drop:与create差不多,所不同的是每次sessionFactory关闭时,就会删除所有表。update(最常用):这个取值比较常用,需要先建立数据库,在第一次加载hibernate时会自动创建表,以后创建hibernate会自动根据model更新表结构,即使表结构改变了,以前的行不会被删除。validate:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 ...

February 27, 2019 · 2 min · jiezi

hibernate实体监听器

上学期学完spring的基本增删改查后,还曾有点小小的得意,感觉自己也算知道不少的编程知识了,但这段时间的项目经验又一次次的教了自己做人,不断出现的新知识让自己目不暇接,知道的越多,未知的越多。也算进入了学习的另一个阶段:知道自己不知道感慨完了,该进入正题了,本周有一个需求,spring sercurity要求在保存密码的时候密码必须加密,学长开始写的时候是在setter密码的时候调用加密方法,不够优雅,就让我把这个方法提出来,换用监听器,当监听的user保存时就自动加密其密码,由于当时只知道拦截器,于是就去找了一篇拦截器的教程,发现写出来感觉相当不合适,就问学长,果然我理解错了意思,是用一个叫实体监听器(Entity Listeners)的东西。Entity Listener顾名思义,它可以监听实体的某些行为,并进行一部分操作。Hibernate支持的回调注解@PrePersistExecuted before the entity manager persist operation is actually executed or cascaded. This call is synchronous with the persist operation.在数据持久化到数据库之前执行@PreRemoveExecuted before the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.执行数据删除之前调用@PostPersistExecuted after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed.在执行数据插入之后调用@PostRemoveExecuted after the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.在执行数据删除之后调用@PreUpdateExecuted before the database UPDATE operation.在执行数据更新之前调用@PostUpdateExecuted after the database UPDATE operation.在执行数据更新之后调用@PostLoadExecuted after an entity has been loaded into the current persistence context or an entity has been refreshed.在数据从数据库加载并且赋值到当前对象后调用用法首先,先定义一个操作用的类,并在其中写下你要用的方法,比如下面的方法就代表在用户持久化和更新的时候执行(只需注解就好,不用继承其他类)。然后在实体上声明,就完工了,相当简单。对于上面的代码,初学者可能会对这一段有疑惑 PasswordEncoder passwordEncoder = ContextConfig.getContext().getBean(PasswordEncoder.class);我开始也不明白,还是学长给我讲解的,这是获取PasswordEncoder的对象,学长给我讲了讲才大概明白了hibernate和spring是两个系统,spring管理的对象,hibernate注入不进来。如果不明白可以看看下面这个图。spring的ioc这篇文章写得挺不错,有兴趣可以看看。总结实体监听器虽然只是一个很简单的功能,但却非常强大与实用,同时了解到hibernate和spring并不是一个东西,以前一直以为hibernate是spring的一个小功能,同时对spring总算有了一丁点清晰的认识,总体状态还算满意,感谢学长们的帮助。 ...

February 22, 2019 · 1 min · jiezi