最近的一个项目,使用的是一个古老的开发脚手架平台JEECG,是的,从官网上可以看出这个脚手架是2013年的,现在他们官网主推的是JEECG BOOT,不过现在接手的这个项目是JEECG,项目中用到的持久层框架是Hibernate。由于基本上没用过Hibernate,所以在项目中踩了很多坑,出现问题,在网上找解决方案也非常少,针对最近遇到的一些问题,做个总结:
1,创建session的两种方式的不同
使用SessionFactory创建session的两种方式:使用openSession()这个方法或者getCurrentSession()方法,创建session,但是这两种是有很大差别的
使用openSession()创建的session是需要每次手工关闭session的,即调用session.close()方法。所以在使用@transactional注解的service层,调用dao层增删改查的时候,事务通常会不起作用,因为调用的时候,session已经关闭了,事务不能正常激活,
像这样的写法就不行的,例如下service层中,调用这个save方法,一调用session就关闭了,事务无法参与进去:
Repository("hotelPreOrderDao")
public class HotelPreOrderDaoImpl implements HotelPreOrderDao{
private TmcHotelPreOrder tmcHotelPreOrder=new TmcHotelPreOrder();
@Autowired
SessionFactory sessionFactory;
@Override
public void save(TmcHotelPreOrder tmcHotelPreOrder) {
Session session = sessionFactory.openSession();
session.save(tmcHotelPreOrder);
session.close();
}
@Override
public void update(TmcHotelPreOrder tmcHotelPreOrder) {
Session session = sessionFactory.openSession();
session.update(tmcHotelPreOrder);
session.close();
}
}
这样写的话,在service层的方法中虽然加了@transactional注解,在service层的方法中调用HotelPreOrderDao.save()方法,事务是不起效的,会报错;
所以可以使用另外一种获取session的方法,这种可以在执行完事务后,自动关闭session,如下:
@Repository("hotelPreOrderDao")
public class HotelPreOrderDaoImpl implements HotelPreOrderDao{
private TmcHotelPreOrder tmcHotelPreOrder=new TmcHotelPreOrder();
@Autowired
SessionFactory sessionFactory;
@Override
public void save(TmcHotelPreOrder tmcHotelPreOrder) {
Session session = sessionFactory.getCurrentSession();
session.save(tmcHotelPreOrder);
}
@Override
public void update(TmcHotelPreOrder tmcHotelPreOrder) {
Session session = sessionFactory.getCurrentSession();
session.update(tmcHotelPreOrder);
}
@Override
public void delete(Serializable id) {
Session session = sessionFactory.getCurrentSession();
session.delete(this.findById(id));
}
@Override
public TmcHotelPreOrder findById(Serializable id) {
Session session = sessionFactory.getCurrentSession();
return (TmcHotelPreOrder)session.get(tmcHotelPreOrder.getClass(),id);
}
/**
* 通过hql查询
* @param hql
* @param paranms 条件参数值
* @return 查询结果,多条数据
*/
@Override
public List<TmcHotelPreOrder> findByHQL(String hql, Object... paranms) {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery(hql);
for (int i=0;paranms!=null&&i<paranms.length;i++){
query.setParameter(i,paranms);
}
return query.list();
}
}
sessionFactory是在xml文件中配置的,所以可以直接注入
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.gbiac.hotelier.*"/>
<context:property-placeholder location="classpath:dbconfig.properties"/>
<!--配置c3p0-->
<!--配置c3p0-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username.jeecg}"></property>
<property name="password" value="${jdbc.password.jeecg}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url.jeecg}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="packagesToScan">
<list>
<value>com.gbiac.hotelier.*</value>
</list>
</property>
</bean>
<bean name="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置注解驱动,使可以使用注解来管理事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
同时还有一个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>
<!--配置基本属性-->
<!--hibernate方言-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 在控制台显示执行的sql语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql语句,更易读 -->
<property name="hibernate.format_sql">true</property>
<!--生成数据表策略-->
<!--<property name="hibernate.hbm2ddl.auto">update</property>-->
<!-- 启用二级缓存 -->
<!--<property name="cache.use_second_level_cache">true</property>-->
<!--<!– 设置二级缓存的实现类,即指定使用的插件 –>-->
<!--<property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>-->
<!--<!– 启用查询缓存 –>-->
<!--<property name="cache.use_query_cache">true</property>-->
<!--把session绑定到本地线程-->
<!--<property name="hibernate.current_session_context_class">thread</property>-->
</session-factory>
</hibernate-configuration>
将那个绑定本地线程的配置注释掉,或者删除这个配置项
这样就可以正常用@transactional注解来管理事务,同时session也不用手动去关闭了,getCurrentSession()会在事务提交后,自动关闭session。
service层加@transactional注解来管理事务
2,HQL写法与hibernate支持的原生sql写法的区别
今天本来是要用hql语句写更新语句,结果在建立执行语句时用错了方法
@Override
public Integer updateByHQL(String fcOrderCode,Integer orderStatus,String orderStatusMessage) {
Session session=sessionFactory.getCurrentSession();
// 原生sql方式更新
// String updateSql="update tmc_hotel_order set order_status= :order_status,order_status_message= :order_status_message where fc_order_code= :fc_order_code ";
// int rowNum = session.createSQLQuery(updateSql).setInteger("order_status", orderStatus).setString("order_status_message", orderStatusMessage)
// .setString("fc_order_code", fcOrderCode).executeUpdate();
// HQL方式更新
String hql="update TmcHotelOrder set orderStatus= :order_status,orderStatusMessage= :order_status_message,modifiedTime= :modified_time where fcOrderCode= :fc_order_code ";
// 保留日期和时间需要使用setTimeStamp方法,setDate截断time,SetTime会截断date
int rowNum = session.createQuery(hql).setInteger("order_status", orderStatus).setString("order_status_message", orderStatusMessage)
.setString("fc_order_code", fcOrderCode).setTimestamp("modified_time",new Date()).
executeUpdate();
return rowNum;
}
我最初用的hql语句写的更新语句,表名和字段用的都是实体类的类名和属性名,因为在实体类中已经用JPA注解映射了实体类和数据表的对应关系,hql语句写的是正确的,但是比较坑的地方是下边这样:
String hql="update TmcHotelOrder set orderStatus= :order_status,orderStatusMessage= :order_status_message,modifiedTime= :modified_time where fcOrderCode= :fc_order_code ";
int rowNum = session.createSQLQuery(updateSql).setInteger("order_status", orderStatus).setString("order_status_message", orderStatusMessage)
.setString("fc_order_code", fcOrderCode).executeUpdate();
是的,我用错方法了,我用成了session.createSQLQuery()这个方法,但是这样用的话,是不对的,hibernate的update语句就变成了update TmcHotelOrder ...,但是根本不存在TmcHotelOrder 这个数据表,运行项目,也会报错,说TmcHotelOrder table not exist;因为session.createSQLQuery()这个方法是执行原生SQL语句的,要使用这个方法,表名和字段名需要是真实的表名和字段名,而不应该是映射的实体类名和属性名。
所以后边改过来了,HQL的增删改查,需要使用session.createQuery(hql)这个方法。这样hibernate会自动根据映射关系,在执行update语句时,会自动更改成表名为tmc_hotel_order,属性名也会自动映射成真正的字段名,就和原生sql的增删改查是一样的啦。
实体类时这样的:
package com.gbiac.hotelier.entity;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* 酒店预订表
*/
@Entity
@Table(name = "tmc_hotel_order")
public class TmcHotelOrder implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
/**
* 酒店id
*/
@Column(name = "hotel_id")
private Integer hotelId;
/**
* 房型id
*/
@Column(name = "room_id")
private Integer roomId;
/**
* 价格计划id
*/
@Column(name = "rate_plan_id")
private Integer ratePlanId;
/**
* 入住日期
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Column(name = "check_in_date")
private Date checkInDate;
/**
* 离店日期
*/
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Column(name = "check_out_date")
private Date checkOutDate;
/**
* 预订间数
*/
@Column(name = "room_num")
private Integer roomNum;
/**
* 房间状态 1:有房 2:待查 3:满房
*/
@Column(name = "room_status")
private Integer roomStatus;
/**
* 床型
*/
@Column(name = "bed_type")
private String bedType;
/**
* 总金额
*/
@Column(name = "total_amount")
private Double totalAmount;
/**
* 合作商订单号
*/
@Column(name = "co_order_code")
private String coOrderCode;
/**
* 泰坦云订单号
*/
@Column(name = "fc_order_code")
private String fcOrderCode;
/**
* 预订结果 1-预订成功 2-预订失败
*/
@Column(name = "result", nullable = false)
private Integer result;
/**
* 预订失败的原因
*/
@Column(name = "message")
private String message;
/**
* 订单状态 1:新建 2:处理中 3:已确认 4:已取消
*/
@Column(name = "order_status", nullable = false)
private Integer orderStatus;
@Column(name = "order_status_message")
private String orderStatusMessage;
public String getOrderStatusMessage() {
return orderStatusMessage;
}
public void setOrderStatusMessage(String orderStatusMessage) {
this.orderStatusMessage = orderStatusMessage;
}
/**
* 订单联系人
*/
@Column(name = "link_man", nullable = false)
private String linkMan;
/**
* 联系人电话
*/
@Column(name = "link_phone", nullable = false)
private String linkPhone;
/**
* 邮箱
*/
@Column(name = "email")
private String email;
/**
* 到店时间(格式HH:mm)
*/
@Column(name = "arrive_time", nullable = false)
private String arriveTime;
/**
* 最晚到店时间(格式HH:mm)
*/
@Column(name = "latest_arrive_time", nullable = false)
private String latestArriveTime;
/**
* 供应商编码
*/
@Column(name = "supply_code", nullable = false)
private String supplyCode;
/**
* 客户特殊要求
*/
@Column(name = "special_demand")
private String specialDemand;
/**
* 订单创建时间
*/
@Column(name = "create_time")
private Date createTime;
/**
* 订单修改时间
*/
@Column(name = "modified_time")
private Date modifiedTime;
/**
* 操作者
*/
@Column(name = "operator")
private String operator;
/**
* 备注字段
*/
@Column(name = "remark")
private String remark;
/**
* 主键id
*/
public Integer getId() {
return id;
}
/**
* 主键id
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 酒店id
*/
public Integer getHotelId() {
return hotelId;
}
/**
* 酒店id
*/
public void setHotelId(Integer hotelId) {
this.hotelId = hotelId;
}
/**
* 房型id
*/
public Integer getRoomId() {
return roomId;
}
/**
* 房型id
*/
public void setRoomId(Integer roomId) {
this.roomId = roomId;
}
/**
* 价格计划id
*/
public Integer getRatePlanId() {
return ratePlanId;
}
/**
* 价格计划id
*/
public void setRatePlanId(Integer ratePlanId) {
this.ratePlanId = ratePlanId;
}
/**
* 入住日期
*/
public Date getCheckInDate() {
return checkInDate;
}
/**
* 入住日期
*/
public void setCheckInDate(Date checkInDate) {
this.checkInDate = checkInDate;
}
/**
* 离店日期
*/
public Date getCheckOutDate() {
return checkOutDate;
}
/**
* 离店日期
*/
public void setCheckOutDate(Date checkOutDate) {
this.checkOutDate = checkOutDate;
}
/**
* 预订间数
*/
public Integer getRoomNum() {
return roomNum;
}
/**
* 预订间数
*/
public void setRoomNum(Integer roomNum) {
this.roomNum = roomNum;
}
/**
* 房间状态 1:有房 2:待查 3:满房
*/
public Integer getRoomStatus() {
return roomStatus;
}
/**
* 房间状态 1:有房 2:待查 3:满房
*/
public void setRoomStatus(Integer roomStatus) {
this.roomStatus = roomStatus;
}
/**
* 床型
*/
public String getBedType() {
return bedType;
}
/**
* 床型
*/
public void setBedType(String bedType) {
this.bedType = bedType;
}
/**
* 总金额
*/
public Double getTotalAmount() {
return totalAmount;
}
/**
* 总金额
*/
public void setTotalAmount(Double totalAmount) {
this.totalAmount = totalAmount;
}
/**
* 合作商订单号
*/
public String getCoOrderCode() {
return coOrderCode;
}
/**
* 合作商订单号
*/
public void setCoOrderCode(String coOrderCode) {
this.coOrderCode = coOrderCode;
}
/**
* 泰坦云订单号
*/
public String getFcOrderCode() {
return fcOrderCode;
}
/**
* 泰坦云订单号
*/
public void setFcOrderCode(String fcOrderCode) {
this.fcOrderCode = fcOrderCode;
}
/**
* 预订结果 1-预订成功 2-预订失败
*/
public Integer getResult() {
return result;
}
/**
* 预订结果 1-预订成功 2-预订失败
*/
public void setResult(Integer result) {
this.result = result;
}
/**
* 预订失败的原因
*/
public String getMessage() {
return message;
}
/**
* 预订失败的原因
*/
public void setMessage(String message) {
this.message = message;
}
/**
* 订单状态 1:新建 2:处理中 3:已确认 4:已取消
*/
public Integer getOrderStatus() {
return orderStatus;
}
/**
* 订单状态 1:新建 2:处理中 3:已确认 4:已取消
*/
public void setOrderStatus(Integer orderStatus) {
this.orderStatus = orderStatus;
}
/**
* 订单联系人
*/
public String getLinkMan() {
return linkMan;
}
/**
* 订单联系人
*/
public void setLinkMan(String linkMan) {
this.linkMan = linkMan;
}
/**
* 联系人电话
*/
public String getLinkPhone() {
return linkPhone;
}
/**
* 联系人电话
*/
public void setLinkPhone(String linkPhone) {
this.linkPhone = linkPhone;
}
/**
* 邮箱
*/
public String getEmail() {
return email;
}
/**
* 邮箱
*/
public void setEmail(String email) {
this.email = email;
}
/**
* 到店时间(格式HH:mm)
*/
public String getArriveTime() {
return arriveTime;
}
/**
* 到店时间(格式HH:mm)
*/
public void setArriveTime(String arriveTime) {
this.arriveTime = arriveTime;
}
/**
* 最晚到店时间(格式HH:mm)
*/
public String getLatestArriveTime() {
return latestArriveTime;
}
/**
* 最晚到店时间(格式HH:mm)
*/
public void setLatestArriveTime(String latestArriveTime) {
this.latestArriveTime = latestArriveTime;
}
/**
* 供应商编码
*/
public String getSupplyCode() {
return supplyCode;
}
/**
* 供应商编码
*/
public void setSupplyCode(String supplyCode) {
this.supplyCode = supplyCode;
}
/**
* 客户特殊要求
*/
public String getSpecialDemand() {
return specialDemand;
}
/**
* 客户特殊要求
*/
public void setSpecialDemand(String specialDemand) {
this.specialDemand = specialDemand;
}
/**
* 订单创建时间
*/
public Date getCreateTime() {
return createTime;
}
/**
* 订单创建时间
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
/**
* 订单修改时间
*/
public Date getModifiedTime() {
return modifiedTime;
}
/**
* 订单修改时间
*/
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
/**
* 操作者
*/
public String getOperator() {
return operator;
}
/**
* 操作者
*/
public void setOperator(String operator) {
this.operator = operator;
}
/**
* 备注字段
*/
public String getRemark() {
return remark;
}
/**
* 备注字段
*/
public void setRemark(String remark) {
this.remark = remark;
}
@Override
public String toString() {
return "TmcHotelOrder{" +
"id=" + id +
", hotelId=" + hotelId +
", roomId=" + roomId +
", ratePlanId=" + ratePlanId +
", checkInDate=" + checkInDate +
", checkOutDate=" + checkOutDate +
", roomNum=" + roomNum +
", roomStatus=" + roomStatus +
", bedType='" + bedType + '\'' +
", totalAmount=" + totalAmount +
", coOrderCode='" + coOrderCode + '\'' +
", fcOrderCode='" + fcOrderCode + '\'' +
", result=" + result +
", message='" + message + '\'' +
", orderStatus=" + orderStatus +
", orderStatusMessage='" + orderStatusMessage + '\'' +
", linkMan='" + linkMan + '\'' +
", linkPhone='" + linkPhone + '\'' +
", email='" + email + '\'' +
", arriveTime='" + arriveTime + '\'' +
", latestArriveTime='" + latestArriveTime + '\'' +
", supplyCode='" + supplyCode + '\'' +
", specialDemand='" + specialDemand + '\'' +
", createTime=" + createTime +
", modifiedTime=" + modifiedTime +
", operator='" + operator + '\'' +
", remark='" + remark + '\'' +
'}';
}
}
表字段是这样的:
控制台中,使用hql方式执行的更新,语句输出在控制台中是这样的:
先暂时更新到这里。现在不早了,这个月先总结这些吧,最近很忙,写博客的时间很少!