最近疫情严重,公司无限延长开工时间,心情也跟着变得沉重。先祝愿武汉早日渡过难关,加油武汉,加油中国!
过年回家,想了想关于自己未来的目标规划,发现自己陷入了死胡同,本来计划一直更新的博客也停止了更新,姐姐劝我博客可以继续写,对自己有好处。所以,在2020/2/10一点,继续更新我的博客。
背景:当前市场上,由于各种项目都比较大,可用性和优化要求都较高,于是持久层框架大家都更偏向于Mybatis而忽略了曾经也风靡一时hibernate。让我感受最深的今天群里一个做了8年开发的前辈,在我们讨论H和M差别时,他说了一句“Hibernate这么垃圾的框架还有人用么?还有你们说的hql是什么鬼?”所以我觉得很多人其实并不了解它,甚至觉得学它毫无用处。确实,能熟练使用mybatis也能完成各种项目,也是市场最流行的,但是个人建议,每个框架存在都有它优点,愿意了解下的就继续看看,当然,本人技术有限,欢迎各位批评和指导。
1:hibernate介绍
1.1,hibernate是什么:Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。(此处引用百度百科的说明)
1.2,hibernate能做什么:上一段话已经大致介绍了,hibernate是一个持久层ORM框架,它可以帮我们做数据库的CRUD,当我们做了对象关系的映射,我们就可以通过操作对象的方式来操作数据库。完全不用自己再来写sql语句,这对不喜欢写sql的人来说是一个福音把。
1.3,和mybatis的差别:在我自己理解,M和H都是ORM框架,而差别在于H的映射做的更加完整而且联合JPA使用映射更加简单,也让人更好理解,而M需要自己通过sql语句和xml文件或者annotation使用,sql语句直接操作的还是表和字段,并不是对象。那么为啥更多人倾向于M呢?还是因为H的映射,现在大多项目都较为庞大,而Hibernate的映射让他在大型项目中显得过于笨重,占用的资源过高,拖累了项目的运行速度。
1.4,hibernate该在什么地方使用:在我自己感觉中,H和M在什么地方都可以使用,如果真要纠结,那么在中小型项目,特别是管理系统之类的使用,更让程序员更加感到善意。
2:hibernate具体使用
2.1,hibernate使用需要些什么;以下是我自己去年做一个管理系统时的ssh全部配置文件,可以copy简单修改就能使用。
pom依赖:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j-version>1.7.25</slf4j-version>
<spring.version>5.0.4.RELEASE</spring.version>
<hibernate.version>5.2.10.Final</hibernate.version>
<aspectj.version>1.8.12</aspectj.version>
</properties>
<dependencies>
<!-- Aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- spring整合Aspectj -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springTest -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- json解析 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- self4j日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4j-version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- 二级缓存ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.4</version>
</dependency>
<!-- spring整合JPA -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0数据源 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.4</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
<!-- java1.8插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
ssh的applicationContext.xml核心配置:包名等等自己按照项目名称做简单修改,我着只是示列。
```yaml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 1.注解扫描包,加载bean -->
<context:component-scan base-package="com.dream">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.加载属性文件 -->
<bean id="annotationPropertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:init.properties</value>
</list>
</property>
</bean>
<!-- 3.配置数据源:jdbc+pool -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 请求超时时间 -->
<property name="checkoutTimeout" value="30000" />
<!-- 每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
<property name="idleConnectionTestPeriod" value="30" />
<!-- 连接数据库连接池最大空闲时间 -->
<property name="maxIdleTime" value="30" />
<!-- 连接池初始化连接数 -->
<property name="initialPoolSize" value="5" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
<property name="acquireIncrement" value="5" />
</bean>
<!-- 4.配置hibernate的SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 注入数据源 相关信息看源码 -->
<property name="dataSource" ref="dataSource" />
<!-- hibernate配置信息 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<!-- 开启二级缓存 ehcache -->
<prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop>
<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory_class}</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.cache.provider_configuration_file_resource_path}
</prop>
</props>
</property>
<!-- 扫描hibernate注解配置的entity
<property name="packagesToScan" value="com.qf.wobb.model" />-->
<property name="packagesToScan">
<list>
<value>com.dream.ssh.po</value>
</list>
</property>
</bean>
<!-- 5.配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
init.properties数据库参数:
#mysqldatabasesetting
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/myssh?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
#hibernateconfig
hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=true
hibernate.format_sql=false
hibernate.hbm2ddl.auto=update
hibernate.cache.use_second_level_cache=false
hibernate.cache.use_query_cache=false
hibernate.current_session_context_class=thread
hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
hibernate.cache.provider_configuration_file_resource_path=ehcache.xml
log4j.properties:log4j打印配置
###setloglevels###INFO
log4j.rootLogger=INFO,stdout,E
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}%5p %c{1}:%L-%m%n
log4j.logger.org.quartz=INFO
log4j.appender.E=org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File=logs/mylogs.log
log4j.appender.E.DatePattern=yyyy-MM-dd'.log'
log4j.appender.E.Threshold=INFO
log4j.appender.E.layout=org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-ddHH\:mm\:ss}[%c][%t\:%r]-[%p]%m%n
springMVC配置文件:
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!-- 1.启动mvc注解 -->
<mvc:annotation-driven conversion-service="conversionService" />
<!--配置ConversionService -->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="dateConverter" />
</set>
</property>
</bean>
<!-- 2.扫描controller -->
<context:component-scan base-package="com.dream.ssh.controller" />
<!-- 3.不拦截静态资源 -->
<mvc:resources mapping="/sb-admin2/**" location="/sb-admin2/"></mvc:resources>
<!-- 4.视图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- jsp所在的位置 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- jsp文件的后缀名 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 统一异常处理 -->
<aop:config>
<aop:advisor advice-ref="aopExceptionHandler" pointcut="execution(* com.dream.ssh.controller..*.*(..))"/>
</aop:config>
</beans>
以上配置文件需要自己针对项目名称,包名等等做做简单修改即可使用。好,接着谈hibernate的使用。
2.2,hibernate具体使用:
hibernate使用核心是session,此处的session和HttpSession不是同一session,你可以把它看成对数据库的一次通信,经过以上配置与spring集成后只需要在dao层使用@Resource或者@Autowired 注解就可以注入SessionFactory
@Resource //@Autowired
private SessionFactory sessionFaction;
Session session = sessionFaction.getCurrentSession();
这样就可以得到与当前线程绑定的session。
2.3,hibernate的几种常见查询方式:
既然拿到了session,就可以具体操作了,hibernate常见几种查询方式有:
2.3.1:自带的crud方法:
session.get()//获取
session.load()//获取
session.update()//修改
session.save()//添加,保存
session.delete()//删除
简单对单一对象操作可使用
2.3.2:QBC查询
session.createCriteria()//不常用,自己试验下就好。
2.3.3:本地sql查询:
session.createNativeQuery(sqlString);//自己写sql语句进行查询,用此方法还不如用Mybatis。
2.3.4:hql查询:个人推荐,hql也就是hibernate query language,hibernate的查询语句;此查询语句和sql有何不同呢?sql是针对的是表,字段,而hql针对的是对象,属性,我们只需要对对象进行操作,hibernate就可以自动生成sql语句进行查询;下面是一些实际操作的例子。
我们拿user来做示范:使用jpa注解快速建表和做映射;
@Entity
@Table(name = "t_user")
public class User {
private final static Logger LOG = LogManager.getLogger(User.class);
```java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 26, unique = true, nullable = false)
private String loginName;
@Column(length = 50, nullable = false)
private String password;
@Column(length = 26)
private String name;
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@ManyToMany
@JoinTable(name = "t_user_role" , joinColumns = @JoinColumn(name = "user_id") ,
inverseJoinColumns = @JoinColumn(name = "role_id"))
private List<Role> roles = new ArrayList<Role>();
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
@Entity //申明实体类
@Table(name = “user”) //数据库中user表,name:表名
@Id //申明主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键产生的方式,GenerationType.IDENTITY为mysql默认,其他数据库可选。
@Column(length = 26, unique = true, nullable = false) //设置属性对应字段的参数,可选name字段名,length长度,unique是否唯一,nullable是否为空等等。
@Temporal(TemporalType.TIMESTAMP) //此处为前端时间格式化,与hibernate无关。
@ManyToMany
@JoinTable(name = “t_user_role” , joinColumns = @JoinColumn(name = “user_id”) ,
inverseJoinColumns = @JoinColumn(name = “role_id”))//多对多维护关系数据一方的配置,另一方为:
@ManyToMany(mappedBy = “users”)
使用了这些annotation,就可以运行下项目,hibernate会自动建表,然后我们就可以愉快的CRUD了:
比如:sessionFactory.getCurrentSession().save(uPo);使用save插入一个user
或者:User user = findById(id);
sessionFactory.getCurrentSession().delete(user);删除一个user,此处删除的入参是对象,所以先查询了下此对象。
接着就是等待已久hql查询了:
test01:通过登录名查询user
@Resource
private SessionFactory sessionFactory;
@Override
//通过登录名查询user
public User findByLoginName(String username) {
String hql = "from User u where u.loginName = :loginName";
Query<User> query = sessionFactory.getCurrentSession().createQuery(hql, User.class);
User user = query.setParameter("loginName", username).uniqueResult();
return user;
}
test02:查询所有user
@Override
public List<User> findAll() {
//查询所有user
String hql = "from User";
List<User> list = sessionFactory.getCurrentSession().createQuery(hql, User.class).list();
return list;
}
test03:模糊匹配并做分页与排序,start分页起始参数,length返回数据最大个数,search模糊匹配值,dir排序参数,asc – 升序desc – 降序
@Override
public List<User> findAllByPage(Integer start, Integer length, String search,String dir) {
LOG.info(dir);
String hql = "from User u where u.name like :name or u.loginName like :name order by u.loginName " + dir;
List<User> list = sessionFactory.getCurrentSession().createQuery(hql, User.class).setParameter("name", "%" + search + "%").setFirstResult(start).setMaxResults(length).list();
return list;
}
看了上面几个例子,是不是觉得更直观的感受到hibernate操作对象的方式去操作表和字段?什么,还么有?那么我们再来看看hql和sql的差别;
hql:
from User u where u.name like '%001%' or u.loginName like '%001%' order by u.loginName asc
相同的sql:
select * from t_user u where u.name like '%001%' or u.loginName like '%001%' order by u.loginName asc
hql查询省略了select *,且User与sql中t_user不同,hql中User为对象User,而sql中的t_user为表,hql中的name,loginname等都为user对象的属性,而sql中的name和loginname等都为表的字段,所以hibernate是通过操作对象的方式对表进行操作,而具体的sql语句由hibernate底层通过我们的对象关系映射去书写并且发送,获取的返回的值也是一个user对象的集合,我们需要关心的只有对象和属性,而且我们再查询出user后只需要getRoles()就可以得到关联的角色。再对比mybatis中的sql语句:
select u.*,
r.id as r_id,
r.name as r_name,
r.description as r_description
from t_user u left join t_user_role ur on (u.id = ur.user_id)
left join t_role r on (ur.role_id = r.id)
where u.id = #{id}
可以发现省去了许多写sql的过程。
2.4,hibernate的一些常用知识点:
2.4.1:一级缓存和二级缓存:hibernate一级缓存就是session级别的缓存,当session关闭时,缓存清空。二级缓存就是session factory级别的缓存,缓存数据存放在session factory中,需要做配置,上面核心配置中有注释开启二级缓存。
2.4.2:惰性加载,hibernate默认为懒加载,即不用不查询,使用时再进行一次查询,可在xml文件或者@ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
中设置为迫切加载fetch = FetchType.EAGER
2.4.3:级联,级联也就是对当前对象进行CRUD时是否对关系数据同步进行修改。此处:cascade = CascadeType.ALL就是设置所有操作同步,可单独设置修改同步删除同步等等。
当然还有事务,已经在配置文件中配置了,service层一个注解@Transactional就搞定了。主要可以关注的就是hql查询把,也是hibernate使用最多的查询方式,hql与sql区别不大,把表换成对象,字段换成属性就ok了。hibernate作为当前不太流行的框架,各有各的选择把,有喜欢用的可以了解一下,不喜欢的mybatis也够用了,半夜写博客,总感觉困困的,很多东西没写出来,也有很多东西写的不明确不好,欢迎指出,欢迎批评,欢迎交流,后边会跟着写写一些常用框架,比如Mybatis,spring,springMVC,shiro,springboot,dubbo,springcloud等等。一起学习知识,一起分享知识。