%d{ABSOLUTE} %5p %c{1}:%L - %m%n
输出为:21:57:26,197 INFO SessionFactoryImpl:927 - closing
例如2:log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
输出为:closingCompany.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Company" table="t_company">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname" type="string"></property>
<!-- 一方的配置:一对一,特殊的多对一。
one-to-one:默认使用主键同步策略完成一对一的表关系体现。
但是我们现在使用的是主外键引用来完成一对一的表关系体现。那么需要进行修正。
使用 property-ref="company" 值是对方引用本方的属性名称
-->
<one-to-one name="address" class="Address" property-ref="company"></one-to-one>
</class>
</hibernate-mapping>
Address.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Address" table="t_address">
<id name="aid" column="aid">
<generator class="native"></generator>
</id>
<property name="aname" column="aname" type="string"></property>
<!-- 多方的配置:一对一,特殊的多对一。外键唯一(外键不能重复),提供外键名称,unique默认值是false -->
<many-to-one name="company" class="Company" column="company_id" unique="true"></many-to-one>
</class>
</hibernate-mapping>
测试代码如下:
package com.itheima.a_one2one;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Address;
import com.itheima.domain.Company;
import com.itheima.utils.HibernateUtils;
// 演示:一对一
public class Demo1 {
@Test
// 一对一保存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Company c = new Company();
c.setCname("传智播客");
Address a = new Address();
a.setAname("北京市朝阳区平房乡红门村28号");
// 注意:在一对一使用外键引用的时候,即情况1,外键所在的对象才能维护外键关系,另一方无法维护外键关系。
a.setCompany(c); // 维护外键关系
// 注意:在一对一使用主外键同步的时候,即情况2,双方都能维护外键关系。
// a.setCompany(c); // 维护外键关系
// c.setAddress(a); // 维护外键关系
session.save(c);
session.save(a);
session.getTransaction().commit();
session.close();
}
@Test
// 一对一查询
// 注意:Hibernate中一对一查询,一定会使用表连接查询,所以也就没有懒加载的问题了。
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Company c = (Company) session.get(Company.class, 1);
System.out.println(c);
session.getTransaction().commit();
session.close();
}
}
Company.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Company" table="t_company">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname" type="string"></property>
<!-- 一方的配置:一对一,真正的一对一。不需要进行修正了。
one-to-one:默认使用主键同步策略完成一对一的表关系体现。我们现在就使用默认的策略。
注意:此时可以使用以下3个属性。
cascade lazy fetch
-->
<one-to-one name="address" class="Address" ></one-to-one>
</class>
</hibernate-mapping>
Address.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Address" table="t_address">
<id name="aid" column="aid">
<!-- foreign:声明该键既是主键又是外键 -->
<generator class="foreign">
<!-- 当本方作为外键时,注入的是引用对方的属性名称 -->
<param name="property">company</param>
</generator>
</id>
<property name="aname" column="aname" type="string"></property>
<!-- 一方的配置:一对一,真正的一对一。既是主键又是外键,需要在某一方声明外键 ,constrained表示约束,默认值是false-->
<one-to-one name="company" class="Company" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
测试代码同上:
缓存(Cache):是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘
(永久性数据存储源)的频率,从而提高应用的运行性能
。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存
。
缓存:程序 <-- (内存) --> 硬盘
一级缓存
:Session 级别缓存,在一次请求中共享数据。(当前有多少个线程连接到数据库,就会有多少个一级缓存。)
二级缓存
:SessionFactory 级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。(二级缓存中存放的是经常使用的、不经常被修改的数据。)内置缓存
:使用一个Map,用于存放配置信息
,如预定义的HQL语句等,提供给Hibernate框架自己使用,对外只读。不能操作。
外置缓存
:使用另一个Map,用于存放用户自定义数据
。默认不开启。对于外置缓存,Hibernate只提供规范(接口),需要第三方实现类,所以我们使用二级缓存,还得导入第三方的jar包。外置缓存又称为二级缓存。二级缓存就是由4部分构成: 类级别缓存 集合级别缓存 时间戳缓存 查询级别缓存(二级缓存的第2大部分:三级缓存) 内部结构如下所示:
访问策略:读写型(read-write)、只读型(read-only)
配置即操作:亦即使用二级缓存提供商的提供的jar。
先在 hibernate.properties 中找到对应的键和值:
再在 hibernate.cfg.xml 中配置开启二级缓存:
先在 hibernate.properties 中找到对应的键和值:
再在 hibernate.cfg.xml 中配置确定二级缓存提供商:
在 hibernate.cfg.xml 中确定 类级别缓存
和 集合级别缓存
配置项:
先确定这两个缓存所在配置文件中的位置:
具体配置:
ehcache-failsafe.xml
文件
ehcache-failsafe.xml
重命名 ehcache.xml
ehcache.xml
,拷贝到src下
ehcache.xml
文件中无用的注释,得到清爽的 ehcache.xml
文件,文件内容如下:ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
一般,该文件的配置不是我们的工作,我们使用默认的配置即可,如果想配置,请看下文的 3.4、ehcache配置文件详解
。
示例代码如下:见类缓存中的示例代码
示例代码如下:
package com.itheima.a_one2one;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;
// 演示:二级缓存
public class Demo1 {
@Test
// 演示:证明二级缓存存在 + 二级缓存中的类缓存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 证明二级缓存存在
Customer c1 = (Customer) session.get(Customer.class, 1); // 有select语句
session.clear(); // 清空一级缓存中的内容
Customer c2 = (Customer) session.get(Customer.class, 1); // 没有select语句
// 二级缓存中的类缓存
System.out.println(c1 == c2); // false
// 由以上说明,二级缓存中的类缓存在缓存数据时,并不是以对象的形式进行缓存的,而是缓存的是对象数据的散列,每次从二级缓存中取出数据,会在类缓存中组装成对象,并返回对象。
session.getTransaction().commit();
session.close();
}
}
如下图所示:
示例代码如下:
package com.itheima.a_one2one;
import java.util.Iterator;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;
// 演示:二级缓存操作
public class Demo2 {
@Test
// 演示:二级缓存中的集合缓存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c1 = (Customer) session.get(Customer.class, 1); // 一条 select 语句,查客户,是类
for (Order o : c1.getOrders()) { // 一条 select 语句,查客户的订单,是集合
System.out.println(o.getOname());
}
session.clear(); // 清空一级缓存
// 二级缓存中的集合缓存
Customer c2 = (Customer) session.get(Customer.class, 1); // 没有select语句
// // 增强for遍历
// for (Order o : c1.getOrders()) { // 没有select语句
// System.out.println(o.getOname());
// }
// 迭代器遍历
Iterator<Order> it = c2.getOrders().iterator(); // 没有select语句
while (it.hasNext()) {
Order o = it.next();
System.out.println(o.getOname());
}
// 如果在使用二级缓存的集合缓存的时候,如果把集合对应的类缓存没有配置上去的话,那么在遍历集合中的元素的时候,select语句会发送很多次。
// 由上可知,二级缓存中的集合缓存中放的是对象的OID,每次从二级缓存中取出数据时,会根据IOD先从类缓存中查找OID对应的数据,如果没找到,会拿着OID从数据库中找。
session.getTransaction().commit();
session.close();
}
}
步骤一:开启查询缓存 先在 hibernate.properties 中找到对应的键和值:
再在 hibernate.cfg.xml 中配置开启查询缓存:
步骤二:在查询query对象时,需要设置缓存内容(注意:存放和查询 都需要设置
)
示例代码如下:
package com.itheima.a_one2one;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;
// 演示:二级缓存操作
public class Demo3 {
@SuppressWarnings({ "unused", "unchecked" })
@Test
// 演示:二级缓存中的查询缓存
// 查询缓存是对hql语句查询的缓存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.createQuery("from Customer");
// 使用二级缓存中的查询缓存时,需要单独再打开
query.setCacheable(true); // 查询时,会先从二级缓存中取结果,取不到就执行语句,将执行结果放入二级查询缓存中
List<Customer> list = query.list(); // 一条 select 语句
session.clear(); // 清空一级缓存
Query query2 = session.createQuery("select c from Customer c");
query2.setCacheable(true);
List<Customer> list2 = query2.list(); // 没有select语句
session.getTransaction().commit();
session.close();
}
}
示例代码如下:
package com.itheima.a_one2one;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;
// 演示:二级缓存操作
public class Demo4 {
@SuppressWarnings("unused")
@Test
// 演示:时间戳缓存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c1 = (Customer) session.get(Customer.class, 1); // 一条 select 语句
session.createQuery("update Customer set cname=:cname where cid =:cid").setString("cname", "rose").setInteger("cid", 1).executeUpdate(); // 使用HQL绑定参数
session.clear(); // 清空一级缓存
Customer c2 = (Customer) session.get(Customer.class, 1); // 又一条 select 语句,因为所有的操作都会在时间戳中进行记录,如果数据不一致(时间戳不一样),将触发select语句进行查询
session.getTransaction().commit();
session.close();
}
}
二级缓存中类缓存、集合缓存、查询缓存、时间戳缓存的总结图:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
<!--
<diskStore path="java.io.tmpdir"/> 设置临时文件存放位置。(缓存一般写入内存,一定程度时,写入硬盘。)
缓存详细设置
<defaultCache /> 所有的缓存对象默认的配置
<cache name="类"> 指定对象单独配置
参数设置
maxElementsInMemory="10000" 内存最大数(类内存中存储对象数据的散列的最大数)
eternal="false" 是否永久(内存常驻留)
timeToIdleSeconds="120" 对象在内存中最多空闲多少秒
timeToLiveSeconds="120" 对象在内存中最多存活多少秒
overflowToDisk="true" 内存满了,是否写入到硬盘
maxElementsOnDisk="10000000" 硬盘最大数(硬盘中存储对象数据的散列的最大数)
diskPersistent="false" 关闭JVM,是否将内存保存硬盘中
diskExpiryThreadIntervalSeconds="120" 轮询
memoryStoreEvictionPolicy="LRU"
Least Recently Used (specified as LRU)
First In First Out (specified as FIFO)
Less Frequently Used (specified as LFU)
•maxElementsInMemory 设置基于内存的缓存中可存放的对象最大数目
•eternal 设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性,默认值是false
•timeToIdleSeconds 设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
•timeToLiveSeconds 设置对象生存最长时间,超过这个时间,对象过期。
如果此值为0,表示对象可以无限期地存在于缓存中。该属性值必须大于或等于 timeToIdleSeconds属性值。
•overflowToDisk 设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中 。
•diskPersistent 当jvm结束时是否持久化对象,默认是false
•diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间。
•memoryStoreEvictionPolicy 当内存缓存达到最大,有新的element加入的时候,移除缓存中element的策略。
默认是LRU(移除最近最少使用的元素),可选的有LFU(移除最不常使用的元素)和FIFO(先进先出) 。
-->
如下图所所示:在4.2中
如下图所所示:
如下图所所示:
如下图所所示:
如下图所所示:
将静态的 listTopics.html
和 showTopic.html
文件改为动态的 listTopics.jsp
和 showTopic.jsp
文件
发帖图解:
根据条件查询所有帖子列表图解:
点击主题,显示对应帖子内容和所有回帖内容图解: 回帖回复以及显示图解: 不在赘述,且看详细代码。
示例代码如下: TopicDao.java
package com.itheima.dao;
import java.util.List;
import com.itheima.domain.Reply;
import com.itheima.domain.Topic;
public interface TopicDao {
/**
* 保存帖子
*
* @param t
*/
void save(Topic t);
/**
* 根据条件查询帖子列表
*
* @param condition
* @return
*/
List<Topic> getAll(String condition);
/**
* 根据帖子id查询帖子
*
* @param tid
* @return
*/
Topic getTopic(Integer tid);
/**
* 保存回帖
*
* @param r
*/
void saveReply(Reply r);
}
TopicDaoImpl.java
package com.itheima.dao;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import com.itheima.domain.Reply;
import com.itheima.domain.Topic;
import com.itheima.utils.HibernateUtils;
public class TopicDaoImpl implements TopicDao {
@Override
public void save(Topic t) {
Session session = HibernateUtils.getCurrentSession();
session.save(t);
}
@Override
public List<Topic> getAll(String condition) {
// 1、获取session对象
Session session = HibernateUtils.getCurrentSession();
// 2、创建query对象
Query query = session.createQuery("from Topic where title like :condition");
// 3、 设置参数,拼接sql语句
if (condition != null && !"".equals(condition.trim())) {
query.setString("condition", "%" + condition + "%"); // 模糊查询,匹配的是帖子标题
} else {
query.setString("condition", "%"); // 模糊查询
}
// 4、执行list
return query.list();
}
@Override
public Topic getTopic(Integer tid) {
// 1、获取session对象
Session session = HibernateUtils.getCurrentSession();
// 2、创建query对象
Query query = session.createQuery("from Topic where tid=:tid");
// 3、 设置参数,拼接sql语句
query.setInteger("tid", tid);
// 4、执行list
return (Topic) query.uniqueResult(); // 查询一条数据
}
@Override
public void saveReply(Reply r) {
Session session = HibernateUtils.getCurrentSession();
session.save(r);
}
}
TopicAction.java
package com.itheima.web.action;
import java.util.Date;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import com.itheima.dao.TopicDao;
import com.itheima.dao.TopicDaoImpl;
import com.itheima.domain.Reply;
import com.itheima.domain.Topic;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class TopicAction extends ActionSupport{
private Topic topic;
private TopicDao td = new TopicDaoImpl();
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
// 发帖子
public String add() {
// 1、手动添加ip地址(作者/发帖人)
topic.setIpAddr(ServletActionContext.getRequest().getRemoteAddr());
// 2、手动添加发帖时间(创建时间)
topic.setCreateDate(new Date());
// 3、手动添加最后回帖时间
topic.setLastReplyDate(topic.getCreateDate());
// 4、 调用dao保存帖子
td.save(topic);
// 5、重定向到帖子列表页面
return "listTopic";
}
private String condition;
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
// 显示帖子列表
public String list() {
// 1、调用dao获得帖子列表 (查询结果)
List<Topic> list = td.getAll(condition);
// 2、将帖子列表(查询结果)放入ActionContext中
ActionContext.getContext().put("list", list); // 把数据直接存到了大Map中,其key的值为list
// 3、转发至list页面进行显示
return "list";
}
private Integer tid;
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
// 根据帖子id显示帖子内容+回帖内容
public String getTopicById() {
// 1、调用dao获得帖子 (查询结果)
Topic topic = td.getTopic(tid);
// 2、将帖子(查询结果)放入ActionContext中
ActionContext.getContext().put("topic_show", topic); // 把数据直接存到了大Map中,其key的值为topic
// 3、转发至show页面进行显示
return "show";
}
private Reply reply;
public Reply getReply() {
return reply;
}
public void setReply(Reply reply) {
this.reply = reply;
}
// 回复帖子+显示回帖
public String replyTopic() {
// 1、手动添加ip地址(作者/发帖人)
reply.setIpAddr(ServletActionContext.getRequest().getRemoteAddr());
// 2、手动添加回帖时间(创建时间)
reply.setCreateDate(new Date());
// 3、手动设置回帖与帖子的关联关系
reply.setTopic(td.getTopic(tid));
// 4、 调用dao保存回帖
td.saveReply(reply);
// 调用dao获得帖子 (查询结果)
Topic topic = td.getTopic(tid);
// 将帖子(查询结果)放入ActionContext中
ActionContext.getContext().put("topic_show", topic); // 把数据直接存到了大Map中,其key的值为topic
// 转发至show页面进行显示
return "show";
}
}
listTopic.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>主题列表</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<!-- 简单搜索表单 -->
<div style="margin: 15px auto; " >
<!-- 搜索表单 -->
<s:form action="topicAction_list" namespace="/" theme="simple" cssClass="simpleSearchForm">
<!-- <form action="" class="simpleSearchForm" onsubmit="alert('暂不支持此功能!');return false;"> -->
<font class="logoLabel">贴吧</font>
<s:textfield name="condition" cssClass="queryString"></s:textfield>
<!-- <input type="text" name="queryString" class="queryString"/> -->
<input type="submit" value="搜 索" />
<!-- </form> -->
</s:form>
</div>
<!-- 菜单 -->
<div class="menubar">
<s:a action="topicAction_list" namespace="/">主题列表
<!-- <a href="listTopic.jsp">主题列表</a> -->
</s:a>
</div>
<!-- 主题列表 -->
<table cellspacing="0">
<tbody class="list topicList">
<!--显示表头-->
<tr class="title">
<td width="25">编号</td>
<td width="25">回复</td>
<td width="500">主题</td>
<td width="110">作者</td>
<td width="145">最后回复时间</td>
</tr>
<s:iterator value="#list" var="t"><!-- 写了var属性,就在大Map中找 -->
<!-- 显示帖子列表 -->
<tr class="data">
<td class="num"><s:property value="#t.tid"/></td>
<td class="num"><s:property value="#t.replySet.size"/></td>
<td>
<s:a action="getTopicById" namespace="/">
<!-- <a href="showTopic.jsp"> -->
<s:param name="tid" value="#t.tid"></s:param>
<s:property value="#t.title"/>
<!-- </a> -->
</s:a>
</td>
<td class="info"><s:property value="#t.ipAddr"/></td>
<td class="info"><s:date name="#t.createDate" format="yyyy-MM-dd hh:mm:ss"/></td>
</tr>
</s:iterator>
<tr>
<td colspan="5" class="num">共有主题数<font color="red"><s:property value="#list.size"/></font>个</td>
</tr>
</tbody>
</table>
<div style="margin-bottom: 15px"></div>
<!--发表主题表单-->
<s:form action="topicAction_add" namespace="/" theme="simple" cssClass="addNewTopicForm">
<!-- <form action="" class="addNewTopicForm">-->
<table class="publishArticleForm">
<tr>
<td>标 题:</td>
<td>
<s:textfield name="topic.title" cssClass="title"/>
<!-- <input type="text" class="title"/> -->
</td>
</tr>
<tr>
<td>内 容:</td>
<td>
<s:textarea name="topic.topicContent" cssClass="topicContent"></s:textarea>
<!-- <textarea name="content" class="content"></textarea> -->
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="发 表" />
</td>
</tr>
</table>
<!-- </form> -->
</s:form>
</body>
</html>
showTopic.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>主题<s:property value="#topic_show.title"/>的回帖专区</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<!-- 简单搜索表单 -->
<div style="margin: 15px auto; " >
<!-- 搜索表单 -->
<s:form action="topicAction_list" namespace="/" theme="simple" cssClass="simpleSearchForm">
<!-- <form action="" class="simpleSearchForm" onsubmit="alert('暂不支持此功能!');return false;"> -->
<font class="logoLabel">贴吧</font>
<s:textfield name="condition" cssClass="queryString"></s:textfield>
<!-- <input type="text" name="queryString" class="queryString"/> -->
<input type="submit" value="搜 索" />
<!-- </form> -->
</s:form>
</div>
<!-- 菜单 -->
<div class="menubar">
<s:a action="topicAction_list" namespace="/">主题列表
<!-- <a href="listTopic.jsp">主题列表</a> -->
</s:a>
</div>
<!-- 当前主题贴数 -->
<div style="padding: 10px 30px; font-size: 12px; font-family:'宋体'">
共有<font color="red"><s:property value="#topic_show.replySet.size"/></font>篇回帖
</div>
<!-- 显示主题 -->
<table class="postList" cellspacing="0">
<tr class="title">
<td width="20" class="num">1</td>
<td><s:property value="#topic_show.title"/></td>
</tr>
<tr class="content">
<td></td>
<td><pre><s:property value="#topic_show.topicContent"/></pre></td>
</tr>
<tr class="info">
<td></td>
<td>
作者:<font color="blue"><s:property value="#topic_show.ipAddr"/></font>
<font color="#999999">发帖时间:<s:date name="#topic_show.createDate" format="yyyy-MM-dd hh:mm:ss"/></font>
</td>
</tr>
</table>
<!-- 显示回复列表 -->
<s:iterator value="#topic_show.replySet" var="ts" status="vs">
<table class="postList" cellspacing="0">
<tr class="title">
<td width="20" class="num"><s:property value="#vs.count+1"/></td>
<td></td>
</tr>
<tr class="content">
<td></td>
<td><pre><s:property value="#ts.replyContent"/></pre></td>
</tr>
<tr class="info">
<td></td>
<td>
作者:<font color="blue"><s:property value="#ts.ipAddr"/></font>
<font color="#999999">回帖时间:<s:date name="#ts.createDate" format="yyyy-MM-dd hh:mm:ss"/></font>
</td>
</tr>
</table>
</s:iterator>
<div style="margin-bottom: 20px"></div>
<!-- 发表回复表单 -->
<s:form action="replyTopic" namespace="/" theme="simple" cssClass="addNewTopicForm">
<!-- <form action="" class="addNewTopicForm"> -->
<s:hidden name="tid" value="%{#topic_show.tid}"></s:hidden>
<table class="publishArticleForm">
<tr>
<td class="label">内 容:</td>
<td>
<s:textarea name="reply.replyContent" cssClass="content"></s:textarea>
<!-- <textarea name="content" class="content"></textarea> -->
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="回 贴"/></td>
</tr>
</table>
<!-- </form> -->
</s:form>
</body>
</html>
效果截图:
问题1、当我们没有整合log4j框架时,控制台出现如下异常:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
七月 16, 2018 5:23:14 下午 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet [jsp] in context with path [/day32_05_Hibernate_tieba] threw exception [Filter execution threw an exception] with root cause
org.hibernate.MappingException: column attribute may not be used together with <column> subelement
at org.hibernate.cfg.HbmBinder.bindColumns(HbmBinder.java:1136)
at org.hibernate.cfg.HbmBinder.bindColumnsOrFormula(HbmBinder.java:1628)
at org.hibernate.cfg.HbmBinder.bindSimpleValue(HbmBinder.java:1208)
at org.hibernate.cfg.HbmBinder.createClassProperties(HbmBinder.java:2206)
at org.hibernate.cfg.HbmBinder.createClassProperties(HbmBinder.java:2164)
at org.hibernate.cfg.HbmBinder.bindRootPersistentClassCommonValues(HbmBinder.java:412)
at org.hibernate.cfg.HbmBinder.bindRootClass(HbmBinder.java:326)
at org.hibernate.cfg.HbmBinder.bindRoot(HbmBinder.java:177)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processHbmXml(Configuration.java:4006)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processHbmXmlQueue(Configuration.java:3998)
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3986)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1398)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1856)
at com.itheima.utils.HibernateUtils.<clinit>(HibernateUtils.java:19)
at com.itheima.web.filter.TransactionFilter.doFilter(TransactionFilter.java:27)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.itheima.web.filter.EncodingFilter.doFilter(EncodingFilter.java:41)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:407)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
因为SLF4J 可以根据我们选择的绑定把日志输出到 log4j 日志框架上,所以我们需要整合log4j,步骤如下:
整合log4j后,异常没有了。
问题2、提交发帖表单时,浏览器出现500错误, Message java.util.Date cannot be cast to java.util.Calendar 如下图所示:
原因是 Reply.hbm.xml 和 Toopic.hbm.xml 配置文件中的
<property name="createDate" column="createDate" type="calendar"></property>
数据类型与数据库中对应的数据类型不符。 解决办法一:可以把数据库中对应的数据类型改为calendar。 解决办法二:可以把该配置文件中这个属性 type="calendar" 删除掉,让数据库它自动识别。 解决办法三:单独设置该列的属性,直接配置成数据库类型(一劳永逸式),代码如下:
<property name="createDate">
<column name="createDate" sql-type="datetime"></column>
</property>
再次提交页面,没有问题了。