SSH做项目遇见的问题,以及解决办法

这篇博客主要总结了SSH(Struts2、Spring、Hibernate)项目中遇到的问题及其解决方法,包括HibernateTemplate的使用注意事项、Struts2值栈数据存取、Action间的跳转、Hibernate一对多配置、文件上传等。在使用过程中,遇到如hibernateTemplate的find方法、findByExample不生效、Action跳转报错等问题,博主提供了详细的解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于三个框架的版本都比较高,所以遇到各种问题!!

1、hibernateTemplate不建议使用find(String hql,Object value)方法

解决办法:用它的findByExample(Obejct entity)方法,里面传一个实体类对象参数即可!需要注意的时,实体类的属性中不要有基本类型的数据,用包装类型;如果是条件查询,就在实体类中封装你的条件,如果是查询所有,就传一个没有设置属性值的实体对象即可(用空参构造new出来的)!

2、hibernateTemplate的findByExample不能通过主键来查找到数据

解决办法:
根据主键查找数据,用hibernateTemplate的get(Class clazz,Serializable id )方法来查询!!前面的参数是实体对象.class,后面一个参数是id值!

3、struts2中值栈数据的存取

(1)存数据:
在action中定义一个变量名字叫xxx,生成getXxx方法,然后在方法中把查询道德结果赋给这个值即可存入值栈中;
如:action类中

private List<Customer> list;
	public List<Customer> getList() {
		return list;
	}

	public String list(){
		list = customerService.findAll();
		return "listSuccess";
	}

(2)取数据:在页面中,可以用EL表达式,但是不建议
首先映入struts2的标签库:

<%@ taglib uri="/struts-tags" prefix="s" %>

然后用struts2提供的标签取数据:

<s:property value="xxx"/>

如:

<!--获取值栈中的数据  -->
<s:iterator value="list" var="cus">
<tr>
	<td>
		<!--遍历这个要加#,直接获取数据不用加-->
		<s:property value="#cus.cusName"/>
	</td>
	<td>
		<s:property value="#cus.cusLevel"/>
	</td>
	<td>
		<s:property value="#cus.msgRes"/>
	</td>
	<td>
		<s:property value="#cus.contacts"/>
	</td>
	<td>
		<s:property value="#cus.fixedTel"/>
	</td>
	<td>
		<s:property value="#cus.mobileTel"/>
	</td>
	<td>
		<a href="${pageContext.request.contextPath}/customer_edit?id=<s:property value="#cus.cusId"/>">编辑</a>
		<a href="${pageContext.request.contextPath}/customer_delete?id=<s:property value="#cus.cusId"/>">删除</a>
	</TD>
</tr>
</s:iterator>
4、跳转Action的时候报错:

There is no Action mapped for namespace [/] and action name [xxxxxx] associated with context path [/xxxxx].
这是因为新版本的struts2用通配符时会出现的问题,所以要在Struts2的配置文件struts.xml的action标签中去配置:

<!--{1}表示第一个通配符*的表示的内容-->
<action name="customer_*" class="customerAction" method="{1}">
	<result name="方法1返回的字符串">方法1执行完想要跳转的页面</result>
	<result name="方法2返回的字符串">方法2执行完想要跳转的页面</result>
	<!--下面这个标签就是新版本struts2用来声明通配符使用的方法的,用了通配符就必须加它-->
	<allowed-methods>方法名1,方法名2</allowed-methods>
	</action>
5、Action之间的相互跳转(转发):

在Struts2的配置文件struts.xml中:

<action name="customer_*" class="customerAction" method="{1}">
	<!--这个type="chain"就是说明这是一个跳转到action的路径-->
	<result name="addSuccess" type="chain">customer_list</result>
</action>
6、HibernateTemplate查询总记录数和实现分页

查询总记录数:
方法一:用hql

//如果需要条件就在后面加上where加条件就行,注意hql中的Customer不是数据库表名,而是数据库表对应的实体类的类名!
public Integer getCount(){
	List<Object> find = (List<Object>) hibernateTemplate.find("select count(*) from Customer");
	hibernateTemplate.findByCriteria(criteria);
	if(find == null || find.size() == 0){
		return 0 ;
	}else{
		//返回总记录数
		return ((Long)find.get(0)).intValue();
	}
}

但是新版本的hibernateTemplate不建议使用hql进行查询!!!
方法二,用DetachedCriteria

public Integer getCount(String cusName) {
	DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
	//表示要查询总记录数
	criteria.setProjection(Projections.rowCount());
	//添加上条件
	criteria.add(Restrictions.like("cusName", "%" + cusName + "%"));
	List<Object> find = (List<Object>) hibernateTemplate.findByCriteria(criteria);
	if(find == null || find.size() == 0){
		return 0 ;
	}else{
		//返回总记录数
		return ((Long)find.get(0)).intValue();
	}
}

分页查询数据:
如果不是条件中没有模糊查询,则可以使用
hibernateTemplate的findByExample(Object entity,int firstResult,int maxResults)方法进行查询!
第一个参数entity是实体类对象(要查询的表对应的实体类),在实体类中封装好条件传入即可;第二个参数firstResult是指从第几条记录开始查询,第一条是0,后面的以此类推;第三个参数maxResults是指每一页显示多少条数据!

如果条件查找中需要模糊查询,则需要用DetachedCriteria

DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
//模糊查询的条件,可以写多条
//如果不是模糊查询,后面Restrictions的方法用add,前一个参数是属性名称,后一个参数是值
criteria.add(Restrictions.like("cusName", "%" + cusName + "%"));
List<Customer> list = (List<Customer>) hibernateTemplate.findByCriteria(criteria,firstResult,maxResults);
7、Hibernate一对多的配置

比如一个客户(Customer)对应多个联系人(LinkMan):
首先在一方的实体类(Customer.java)中加上一个Set集合类型的属性,生成其get和set方法,表示对应多个联系人;

//对应的联系人
Set<LinkMan> setLinkMan = new HashSet<>();


public Set<LinkMan> getSetLinkMan() {
	return setLinkMan;
}

public void setSetLinkMan(Set<LinkMan> setLinkMan) {
	this.setLinkMan = setLinkMan;
}

并且在一方的映射配置文件(Customer.hbm.xml)中配置上该属性:

<!-- 配置一对多的关系 -->
<set name="setLinkMan">
	<!-- 外键 -->
	<key column="clid"></key>
	<!-- 对应的实体类 -->
	<one-to-many class="cn.melo.entity.LinkMan"/>
</set>

然后在多方的实体类(LinkMan.java)中加上一个一方类型的对象,生成其get和set方法,表示当前联系人对应的某个客户:

private Customer customer;
public Customer getCustomer() {
	return customer;
}
public void setCustomer(Customer customer) {
	this.customer = customer;
}

并且在多方的映射配置文件(LinkMan.hbm.xml)上加上该属性:

<!-- 配置多对一的关系 -->
<!-- column是指关联的外键,要和一对多的配置中的外键一样 -->
<many-to-one name="customer" class="cn.melo.entity.Customer" column="clid"></many-to-one>

当然,不要忘了在hibernate的主配置文件中引入这两个配置文件!

8、dao层出现:Could not obtain transaction-synchronized Session for current thread

解决办法:在service的实现类上加上事务的注解!!
当然前提是你在spring的配置文件中配置了事务!!!

9、action中庸模型驱动来获取表单上传的数据封装到对象中:

前提:表单中的name属性值和实体类的属性名称一致!
首先让action类实现ModelDriven借口,重写它的方法getModel,做法是new一个你想要得到的实体类对象在action的成员处!在getModel中直接返回这个对象即可,如:

public class LinkManAction extends ActionSupport 
		implements ModelDriven<LinkMan> {
	private static final long serialVersionUID = 1L;
	
	//模型驱动封装
	private LinkMan linkMan = new LinkMan();
	@Override
	public LinkMan getModel() {
		return linkMan;
	}
}

如果是多对一的情况,如Customer和LinkMan,在表单中只有Customer的id,而你想把这个id给LinkMan的属性customer这个对象,那么你在表单的name属性值就应该写上:

<select name="customer.cusId">......</select>

这里的customer是LinkMan这个实体类中Customer类型的属性的属性名,而cusId则是Customer的属性名!!

10、Struts2实现文件上传

(1)上传图片对表单的要求:
表单的提交方式必须是post;
<form>标签中必须提供属性:enctype="multipart/form-data"
要有文件上传项,即有一个或多个input的属性type的值是file

(2)struts2里面使用拦截器对上传进行封装:
在这里插入图片描述
做法:
A、在action中定义两个变量,第一个变量是java.util.File类型的,变量名和表单中属性值type为file的表单项的name属性值一样,表示上传的文件!
第二个变量是String类型的,变量名是上面那个变量名后面加上FileName!表示上传文件的名称!
然后生成两个变量的get和set方法!!
表单:

<INPUT type="file" id=sChannel2 style="WIDTH: 180px" maxLength=50 name="linkPic">

action中

/*
 * struts2实现文件上传需要定义两个变量
 * 1、是文件File类型,变量名和表单中属性type的值为file的input的name属性值一样
 * 2、是上传的 文件的名字,变量名是上面一个变量的名字后面跟上FileName
 * 生成它们的get和set方法
 */
private File linkPic;
private String linkPicFileName;

public void setLinkPic(File linkPic){
	this.linkPic = linkPic;
}
public File getLinkPic() {
	return linkPic;
}
public String getLinkPicFileName() {
	return linkPicFileName;
}
public void setLinkPicFileName(String linkPicFileName) {
	this.linkPicFileName = linkPicFileName;
}

注意命名规范是必须的!!!!
B、对上传图片进行处理:

//实现文件上传逻辑
if(linkPic != null){
	//为了防止文件名字重复,用uuid重新给文件命名
	String uuid = UUID.randomUUID().toString().
			replace("-", "").toUpperCase();
	String fileName = uuid + "_" + linkPicFileName;
	//为了防止一个文件夹下有太多的文件,所以用hashcode分包
	String dir =Integer.toHexString(fileName.hashCode()) ;
	//前四位作为一级文件夹
	String dir1 = dir.substring(0,4);
	//后几位做二级文件夹
	String dir2 = dir.substring(4);
	//创建文件夹和文件
	File serverFile = new File("D:\\BYSJ\\linkPic\\" + dir1
			+ "\\" + dir2 +"\\" + fileName);
	//实现文件上传,用struts提供的工具类org.apache.commons.io.FileUtils
	FileUtils.copyFile(linkPic, serverFile);
	
	//在数据库中保存图片的路径
	linkMan.setPicture(serverFile.toString());
}

注意:上传的文件的大小默认是不能超过2M的,struts2中有很多默认常量(可以在struts2-core-2.5.20.jar下的org.apache.struts2.default.properties中查看这些常量),文件上传大小就是这就是其中之一:
在这里插入图片描述
所以如果想改变常量的值,就在struts.xml中配置:

<struts>
	<constant name="struts.multipart.maxSize" value="5242880">
	</constant>
	......
</struts>
11、Hibernate的no session问题

多表查询的时候会出现该问题,因为你在第一次查询的时候创建了session对象,当你第二次再用外键去查询另外一个表的时候,就会出现no session问题,解决办法:
让session延迟关闭,即操作完dao之后不马上关闭session,而是在action操作之后再关闭!!!
具体实现:配置一个过滤器即可!!但是注意,必须陪在struts2的那个过滤器StrutsPrepareAndExecuteFilter之前!!
在web.xml:

<filter>
	<filter-name>openSessionInViewFilter</filter-name>
	<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>openSessionInViewFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
12、 hibernate外键双向维护

(1)多表操作的时候,会有外键双向维护,所以,你如果修改了其中一方的某个字段,那么,就会把另一方中相关联的外键值置为null;
(2)解决方式:让其中的一方放弃关系维护(让一那一方放弃):

<!-- 配置一对多的关系 -->
<set name="setLinkMan" inverse="true">
	<!-- 外键 -->
	<key column="clid"></key>
	<!-- 对应的实体类 -->
	<one-to-many class="cn.melo.entity.LinkMan"/>
</set>
13、 hibernate级联删除

在一对多的操作中,加入你要删除一的那一方中,如果你没有配置级联删除,因为在sql中是无法直接删除有外键关联的字段的,所以它会先把多的那方的关联外键偶变为null,再删除;但是如果你配置了inverse=“true”,且没有配置级联删除,那么就不会把外键置为null,所以删除的时候,就会报错!!!!
如果配置了级联删除,就会把多的那一方的相关信息数据页全部删除!!
配置:

<!-- 配置一对多的关系 -->
<set name="setLinkMan" inverse="true" cascade="delete">
	<!-- 外键 -->
	<key column="clid"></key>
	<!-- 对应的实体类 -->
	<one-to-many class="cn.melo.entity.LinkMan"/>
</set>
14、struts2的错误处理机制:

在struts2里面有错误处理机制:如当上传文件超过默认的大小,自动返回结果 input

  • 在struts.xml中配置input的返回结果
    在这里插入图片描述
    当struts2自动返回input结果,出现错误,在配置input的页面中可以查看错误信息
  • 在配置的错误页面中,使用struts2标签可以查看到错误信息
    在这里插入图片描述
15、BaseDao的抽取:

接口BaseDao:

package cn.melo.utils;

import java.io.Serializable;
import java.util.List;

public interface BaseDao<T> {
	
	//添加
	public void add(T t);
	//删除
	public void delete(T t);
	//修改
	public void update(T t);
	
	//查询所有,可以加条件
	public List<T> findAll(T t);
	
	//根据id查一条记录
	public T findOne(Serializable id);
	
	//查询总记录数
	public Long getCount();
	
}

BaseDao接口的实现类:

package cn.melo.utils;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate5.HibernateTemplate;

public class BaseDaoImpl<T> implements BaseDao<T> {

	private HibernateTemplate hibernateTemplate;
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
	
	private Class<T> clazz;
	
	//构造方法
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		//首先获取当前类的Class对象
		@SuppressWarnings("rawtypes")
		Class<? extends BaseDaoImpl> class1 = this.getClass();
		//得到当前类的父类的参数化类型,也就是BaseDaoImpl<T>
		//用Type接口的子接口ParameterizedType
		ParameterizedType type = (ParameterizedType)class1.getGenericSuperclass();
		//得到父类的泛型
		Type[] types = type.getActualTypeArguments();
		
		//Class类实现了Typ接口
		clazz = (Class<T>) types[0];
	}
	@Override
	public void add(T t) {
		hibernateTemplate.save(t);	
	}

	@Override
	public void delete(T t) {
		hibernateTemplate.delete(t);
	}

	@Override
	public void update(T t) {
		hibernateTemplate.update(t);
	}

	@Override
	public List<T> findAll(T t) {
		List<T> list = hibernateTemplate.findByExample(t);
		return list;
	}

	@Override
	public T findOne(Serializable id) {
		T t = (T) hibernateTemplate.get(clazz, id);
		return t;
	}

	@Override
	public Long getCount() {
		//得到离线查询对象
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		//添加查询操作
		criteria.setProjection(Projections.rowCount());
		//查询
		@SuppressWarnings("unchecked")
		List<Object> counts  = (List<Object>) hibernateTemplate.findByCriteria(criteria);
		return (Long)counts.get(0);
	}

}

16、hibernate的多条件查询:
//创建离线查询对象
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
//添加条件
criteria.add(Restrictions.xxx());

Restrictions类中有很多方法用来做条件查询:
如:

//对某个属性精确查询
eq(String propertyName, Object value);
//对某个属性模糊查询
like(String propertyName, Object value);
//属性值不等于的查询
ne(String propertyName, Object value);
//属性值大于的查询
gt(String propertyName, Object value);
//属性值大于等于的查询
ge(String propertyName, Object value);
//属性值小于的查询
lt(String propertyName, Object value);
//属性值小于等于的查询
le(String propertyName, Object value);
//属性值等于空值
isNull(String propertyName);
//属性值等于非空值
isNotNull(String propertyName);
//属性值等于列表中的某个值,后面的参数也可以传List
in(String propertyName, Object... values);
//属性值在某一个闭区间
between(String propertyName, Object low, Object high)
//或
or(Criterion... predicates)
//和
and(Criterion... predicates)
//后面两个的参数,传多个Restrictions调用方法的语句即可
17、hibernateTemplate调用原生sql
//hibernate调用原生的SQL语句查询
public List<User> test(){
	//得到session对象
	SessionFactory factory = hibernateTemplate.getSessionFactory();
	Session session = factory.openSession();
	//写sql语句
	String sql = "sql语句";
	//创建NativeQuery对象
	@SuppressWarnings("unchecked")
	NativeQuery<User> query = session.createSQLQuery(sql);
	//查询
	List<User> list = query.list();
	return list;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值