登录
如果出现:Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove ‘readOnly’ marker from transaction definition错误时。
需要在业务层开启事务,如果需要对数据库进行曾,删,改时
1.在hibernate的配置文件中配置事务管理器,事务增强(开启注解)
<!--配置事务管理器-->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--开启注解 增强-->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.直接在业务类上加入注解
@Transactional
public class CategoryServiceImpl implements CategoryService {
@Setter
private CategoryDao categoryDao;
@Override
public void addCategory(Category category) {
categoryDao.addCategory(category);
}
}
分类
json jar包导入正确
使用data-id=" " 将id绑定到按钮上发送请求时可以携带id过去
<a href="#" class="updatebtn" data-id="<s:property value="#category.cid"/>">
<img class="img_icon" src="${pageContext.request.contextPath}/images/edit_icon.png" alt="">
</a>
使用Ajax(前端技术) 将请求发送给web层
$.post("${pageContext.request.contextPath}/category/category_updateUI.action",{"cid":cid},function (data) {
},"json");
将web层取出的数据直接在页面显示
//将取出的数据转化为json类型
JSONArray jsonArray = JSONArray.fromObject(oneCategory, jsonConfig);
System.out.println(jsonArray.toString());
//将数数据打印到页面
ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
ServletActionContext.getResponse().getWriter().println(jsonArray.toString());
通过json可以直接将数据传到data里,所以取得时候直接用data取就行了
$.post("${pageContext.request.contextPath}/category/category_updateUI.action",{"cid":cid},function (data) {
console.log("-------json------");
console.log(data);
/!*把json数据展示到文本框 *!/
$("#parentid2").val(data[0].cparentid);
$("#cname2").val(data[0].cname);
},"json");
注意:在使用迭代器的时候按钮的标注不能使用id,因为id时唯一的表示,要使用class
<s:iterator value="categoryList" var="category">
<ul class="list_goods_ul">
<li><s:property value="#category.cparentid"/> </li>
<li><s:property value="#category.cname"/></li>
<li>
<a href="#" class="updatebtn" data-id="<s:property value="#category.cid"/>">
<img class="img_icon" src="${pageContext.request.contextPath}/images/edit_icon.png" alt="">
</a>
</li>
<li><a href="#"><img class="img_icon" src="${pageContext.request.contextPath}/images/delete_icon.png" alt=""></a></li>
</ul>
</s:iterator>
样式地址使用绝对路径:${pageContext.request.contextPath}
文章
建立关系映射注意点:有外键的一定要注意在domain里面也要创建一个相应的外键对象
在配置文件中要描述是多对一还是一对多配置好,get,set方法
private Integer article_id;
private String article_title;
private String article_content;
private Integer article_time;
private String article_pic;
private String article_desc;
//外键
private Category category;
<?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>
<class name="com.blog.domain.Article" table="article" >
<!--建立类属性哪一个是主键 还要跟数据库当中主键进行对象-->
<id name="article_id" column="article_id" >
<generator class="native"/>
</id>
<!--建立类中的普通属性与数据库当中字段进行关联-->
<property name="article_title" column="article_title" />
<property name="article_content" column="article_content"/>
<property name="article_time" column="article_time"/>
<property name="article_pic" column="article_pic"/>
<property name="article_desc" column="article_desc"/>
<many-to-one name="category" class="com.blog.domain.Category" column="article_cid"></many-to-one>
</class>
</hibernate-mapping>
注意点:
要是出现错误:
org.hibernate.LazyInitializationException: could not initialize proxy [com.blog.domain.Category#2] - no Session
是由于事务在业务层开启和关闭的当,而用tostring覆盖了category属性的时候,由于使用的是懒加载方式所以会报出没有找到session的错误
解决:
<!--配置延迟加载-->
<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>*.action</url-pattern>
</filter-mapping>
给web.xml文件配置一个延迟加载过滤器,让事务在web层也开启,注意:需要把延迟加载配置在struts的前面
分页
需要数据:当前页的页码(currentPage) 一页展示多少条数据(pageSize)
当前页查询的角标(index) 总的记录数(totalCount) 总的分页数(totalPage)
当前页的数据(pageList)
数据库中查询的位置=(currentPage-1)*pageSize
总页数=totalCount*1.0 / totalPage 向上取整(ceil)
示例代码:
package com.blog.domain;
import java.util.List;
public class PageBean<T> {
/*当前页*/
private Integer currentPage;
/*一页有多少条数据*/
private Integer pageSize;
/*当前查询的角标*/
private Integer index;
/*总的记录数*/
private Integer totalCount;
/*总页数*/
private Integer totalPage;
/*当前页的数据*/
private List<T> pageList;
/*如果当前页没有设置,默认设置为第1页*/
public void setCurrentPage(Integer currentPage) {
if (currentPage == null) {
currentPage = 1;
}
this.currentPage = currentPage;
}
/*如果没有设置当前页总记录数据,设置默认记录数为一页5条*/
public void setPageSize(Integer pageSize) {
if (pageSize == null) {
pageSize = 5;
}
this.pageSize = pageSize;
}
/*计算当前页从数据库当中查询的位置*/
public Integer getIndex() {
return (currentPage-1)*pageSize;
}
/*计算总页数*/
public Integer getTotalPage() {
double ceil = Math.ceil(totalCount * 1.0 / pageSize);
return (int)ceil;
}
}
文章分页注意:
1.创建PageBean
2.创建一个action方法,并由jsp发送action地址
3.web层接受参数,创建离线查询语句,调用业务层处理数据,将数据写入值栈,请求转发
public class ArticleAction extends ActionSupport implements ModelDriven<Article> {
@Override
public Article getModel() {
return article;
}
@Setter
private Article article;
@Setter
private ArticleService articleService;
public String list(){
List<Article> list = articleService.list();
ActionContext.getContext().getValueStack().set("list",list);
return "list";
}
public String add(){
System.out.println(article.getArticle_title());
return null;
}
@Setter
private Integer currentPage;
@Setter
private String keyWord;
public String pageList(){
//离线条件查询
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Article.class);
//添加查询条件这也是为什么要把离线条件查询直接写在web的原因
if (keyWord!=null){
detachedCriteria.add(Restrictions.like("article_title","%"+ keyWord+ "%"));
}
PageBean<Article> pageBean=articleService.getPageList(detachedCriteria,currentPage,5);
ActionContext.getContext().getValueStack().push(pageBean);
return "list";
}
}
4.业务层给pagebean设置属性,调用dao层查询数据,返回一个pageBean给web层
public PageBean<Article> getPageList(DetachedCriteria detachedCriteria, Integer currentPage, int pageSize) {
//设置数据,查询数据库
//1.设置当前页
pageBean.setCurrentPage(currentPage);
//2.设置一页显示的个数
pageBean.setPageSize(pageSize);
/* //3.设置数据库查询的位子
pageBean.setIndex(pageBean.getIndex());*/
//4.设置总的记录数 需要从数据库中查找
Integer totalCount=articleDao.getTotalCount(detachedCriteria);
pageBean.setTotalCount(totalCount);
//5.设置总页数
pageBean.setTotalPage(pageBean.getTotalPage());
//6.设置每页的数据 需要从数据库中查找
List<Article> pageList=articleDao.getTotalList(detachedCriteria,pageBean.getIndex(),pageBean.getPageSize());
pageBean.setPageList(pageList);
return pageBean;
}
5.dao层依据需求查询出数据给业务层
@Override
public Integer getTotalCount(DetachedCriteria detachedCriteria) {
//使用setProjection(Projections.rowCount());查询出总的记录数,别的数据不需要
//要注意的是查询出来的结果是一个Long类型的集合
detachedCriteria.setProjection(Projections.rowCount());
List<Long> list = ( List<Long>)this.getHibernateTemplate().findByCriteria(detachedCriteria);
if(list.size()>0){
return list.get(0).intValue();
}
return 0;
}
@Override
public List<Article> getTotalList(DetachedCriteria detachedCriteria, Integer index, Integer pageSize) {
//由于之前使用过 detachedCriteria.setProjection(Projections.rowCount());所以要把他里面的条件清空
//否者查询的还是之前的条件
detachedCriteria.setProjection(null);
List<Article> list = (List<Article>)this.getHibernateTemplate().findByCriteria(detachedCriteria, index, pageSize);
return list;
}
关键字搜索:
1.参数的回显-----由于请求会将参数存到值栈的非根区的parameters所以取得时候应该要加上#号
<input type="text" class="am-form-field" id="input_search" value="<s:property value="#parameters.keyWord"/>" />
2.绑定参数执行下一页------为了让分页数据可以进行下一页查找,应该将回显参数也传给web层
否者会导致无法查看下一页的查询数据
//分页
$("#page").paging({
pageNo:<s:property value="currentPage"/>,
totalPage: <s:property value="totalPage"/>,
totalSize: <s:property value="totalCount"/>,
callback: function(num) {
var keyWord = $("#input_search").val();
$(window).attr('location','${ctx}/article/article_pageList.action?currentPage='+num+"&keyWord="+keyWord);
}
});
开发者模式
<constant name="struts.devMode" value="true"></constant>
<package name="Article" extends="struts-default" namespace="/article">
<action name="article_*" class="articleAction" method="{1}">
<result name="list" >/mgr_main.jsp</result>
<allowed-methods>list,add,pageList</allowed-methods>
</action>
</package>
在需要打开开发者模式的Struts前面加上
在需要查看debug的jsp加上
<s:debug></s:debug>
就可以点击查看debug值栈里面的数据
图片上传
步骤:
1.监听发布按钮
$("#send").click(function () {
$("#blog_form").submit();
})
2.设置form表单 在form表单后面加上enctype=“multipart/form-data”(文件上传)属性
<form id="blog_form" action="${pageContext.request.contextPath}/article/article_add.action" method="post" enctype="multipart/form-data">
</form>
3.在action里面处理文件上传
3.1设置文件上传需要的属性注入进来
@Setter
private String uploadFileName; // 文件名称
@Setter
private File upload; // 上传文件 upload 临时文件
@Setter
private String uploadContentType; // 文件类型
3.2处理方法里面的文件上传
public String add() throws IOException {
//上传图片
/* System.out.println(uploadFileName);
System.out.println(upload);
System.out.println(uploadContentType);*/
//1.判断文件临时文件是否存在
if (upload!=null){
/* 临时文件存在就开始处理*/
//2.设置文件的上传路径
String path = ServletActionContext.getServletContext().getRealPath("/upload");
/*对文件名称进行处理*/
/* 截取文件后缀名*/
//3.获取截取角标
int index = uploadFileName.lastIndexOf(".");
//4开始截取
String suffixName = uploadFileName.substring(index);
//5.生成随机的名字并去除里面的‘-’
String uuidFileName = UUID.randomUUID().toString().replace("-", "")+suffixName;
//6.判断上传路径是否存在,不存在就创建一个
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
//7.上传文件
File dictFile = new File(path + "/" + uuidFileName);
FileUtils.copyFile(upload,dictFile);
}
return null;
}
文件上传处理步骤:
1.首先判断文件是否存在 if (upload!=null){ }存在的话就处理
2.设置文件的上传路径 ServletActionContext.getServletContext().getRealPath("/upload");
3.随机生成文件名
3.1先获取文件的截取角标
int index = uploadFileName.lastIndexOf(".");
3.2截取文件的后缀
String suffixName = uploadFileName.substring(index);
3.3随机生成文件名并和后缀拼接 还要将里面的‘-’ 去掉
String uuidFileName = UUID.randomUUID().toString().replace("-", “”)+suffixName;
4.判断上传的路径是否存在 如果不存在就创建一个
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
5.上传文件
File dictFile = new File(path + "/" + uuidFileName);
FileUtils.copyFile(upload,dictFile);
注意:导入的file包应该是import java.io.File;
分类下拉框
父框(前面一个)
1.一到添加文章的界面就显示父类的属性,使用json异步加载的办法
1.先发送一个action,携带cparentid=0的参数从数据库获取分类数据
1.1根据cparentid查找。
1.2将查找出来的数据转为json格式设置响应字符的编码并反显到function()中。
public String getCategory() throws IOException {
List<Category> list=articleService.getCategory(cparentid);
//把list转化为json格式回显
JSONArray jsonArray = JSONArray.fromObject(list,new JsonConfig());
//设置响应字符编码
ServletActionContext.getResponse().setContentType("text/html;charset=utf-8");
ServletActionContext.getResponse().getWriter().println(jsonArray.toString());
return null;
}
2.获取的数据回显到function(data) 遍历data并且写入到option里面
$.post("${ctx}/article/article_getCategory.action",{"cparentid":0},function (data) {
console.log(data);
//遍历
$(data).each(function (i, obj) {
$("#category_select").append("<option value="+obj.cid+">"+obj.cname+"</option>");
})
//一进来出发事件
$("#category_select").trigger("change");
},"json");
子框(后面一个)
1.注册一个change事件
2.从父类的option中取出value ,var cid = $("#category_select").val();
3.根据父类的value从数据库中查找出父类对应子类的category
4.将category在子框中显示
注意:
1.将category在子框中显示钱需要将子框里面的数据先清空 $("#skill_select").empty();否者会导致所有的子类叠加
2.在进入添加文章的界面时就需要让父类出发change事件否者一进来子类不会被加载
//一进来出发事件
$("#category_select").trigger("change");
//监听分类改变
$("#category_select").on("change",function () {
var cid = $("#category_select").val();
$.post("${ctx}/article/article_getCategory.action",{"cparentid":cid},function (data) {
$("#skill_select").empty();
$(data).each(function (i, obj) {
$("#skill_select").append("<option value="+obj.cid+">"+obj.cname+"</option>");
})
},"json")
})