ORM客户管理系统

一、搭建环境

1、导入jar包 2、导入配置文件(applicationContext.xml以及struts2.xml以及jdbc.properties和日志信息) 3、在web.xml中指定加载配置文件(监听器以及拦截器)
注解加配置文件进行开发

二、项目开始进行

创建实体类:要注意下面这些注解,这是最基本注解

@Entity
@Table(name="sys_user")
public class User {

    @Id
    @Column(name="user_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long userId;

    @Column(name="user_code")
    private String userCode;

    @Column(name="user_name")
    private String userName;

    @Column(name="user_password")
    private String userPassword;

    @Column(name="user_state")
    private String userState;
    //生成get,set方法
}

创建UserAction:首先继承ActionSupport类然后实现ModelDriven接口,记得加泛型

//类上加上以下注解
@Controller
@Scope("prototype")
@ParentPackage("strutd-default")
@Namespace("/")
@Results({
    @Result(location="/jsp/success.jsp",name="success",type="redirect")
})

写一个方法,当用户注册发送请求时,用来保存用户。需要在该方法上加@Action注解
UserAction类:其中注入了UserService接口

@Controller
@Scope("prototype")
@ParentPackage("struts-default")
@Namespace("/")
@Results({
    @Result(location="/jsp/success.jsp",name="success",type="redirect")
})
public class UserAction extends ActionSupport implements ModelDriven {
    @Autowired
    private UserService userService;

    @Action("userAction_save")
    public String save(){
        //1.获得数据----模型驱动自动封装数据
        //2.调用service保存数据
        userService.save(user);
        return "success";
    }   

    private User user = new User();
    @Override
    public Object getModel() {
        return user;
    }

}

创建UserService接口(不用加注解),创建UserServiceImpl实现类(要加注解@Service以及开启事务注解@Transactional
注入UserDao

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void save(User user) {
        userDao.save(user);     
    }

}

创建UserDao接口,创建UserDaoImpl实现类,记得加注解@Repository

@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    private HibernateTemplate hibernateTemplate;

    @Override
    public void save(User user) {
        hibernateTemplate.save(user);       
    }
}

启动:有错误:There is no Action mapped for namespace [/] and action name [userAction_save发现把Action的save方法设置为private了,真是让人头疼,一定是自己手抖了

使用find()方法的时候,出现空指针异常,我都没有传参,竟然报空指针异常,原来是在Action层中没有注入Service,我真的好气,找了半天的错误

用户名唯一性校验

这里是在用户名的输入框添加onblur事件,要发的是ajax请求,但是我想写的是使用$.post()的方式,结果实际写成了$.ajax(),以至于使用属性封装没有获取到参数值。
在dao层,因为虽然只有一个值,但是find方法返回的仍是一个list,所以需要把count(*)的值获取出来,并转成int格式

return Integer.valueOf(count.get(0).toString());

在执行注册提交的时候,需要在form标签里进行判断能否进行注册,onsubmit=“return checkSubmit()” 再在表单任意位置写一个input隐藏域,其中根据其value值来进行判断提交还是不提交,
这里我把input的id值和提交方法的方法名写为了同一个名字,结果导致方法没有执行。

用户退出模块

友好显示,询问是否退出,添加onclick事件,但是因为使用的是a标签,所以要让a标签的href属性失效,所以这时需要href="javascript:void(0)",即阻止后续运行。
js访问服务器,使用location.href=""
Action 中将session清除,返回到login.jsp页面,因为index页面使用的是框架集,会导致只有顶部进行跳转,下面还是index的页面,此时需要在login.jsp页面进行添加如下js代码。

<script type="text/javascript">
    window.onload=function(){
        if(top.location!=self.location){//判断地址栏的地址和自己的地址是否一致
            //不一致,将自己的地址赋给地址栏地址,这样就直接跳到了login.jsp页面
            top.location=self.location;
        }
    }
</script>

修改密码模块

用户名框不能够允许被修改,所以input框加上属性readonly="readonly"
这里使用的是hibernate,直接使用update方法,所以传入的是修改后的User对象,此时user从session中获取,密码从模型驱动中获取,然后set password进入从session中获取的user对象里,

字典表

整合表又称为字典表,字典表中的数据就是共有数据。
字典表

创建客户表,以及字典表,客户表中的某些字段需要引用字典表

//特殊字段
    //@JoinColumn(name="数据库中新增列的列名",referencedColumnName="指向主表的主键,即外键指定的那个表就是主表")
    @JoinColumn(name="cust_source",referencedColumnName="dict_id")
    @ManyToOne(targetEntity=BaseDict.class)
    private BaseDict baseDictSource;

    @JoinColumn(name="cust_industry",referencedColumnName="dict_id")
    @ManyToOne(targetEntity=BaseDict.class)
    private BaseDict baseDictIndustry;

    @JoinColumn(name="cust_level",referencedColumnName="dict_id")
    @ManyToOne(targetEntity=BaseDict.class)
    private BaseDict baseDictLevel;
    baseDictLevelList = baseDictService.findBaseDictByTypeCode("006");
        baseDictSourceList = baseDictService.findBaseDictByTypeCode("009");
        baseDictIndustryList = baseDictService.findBaseDictByTypeCode("001");

查询到这些list以后,存放到值栈中,可以手动或者自动,自动:即是使用属性封装,然后在页面使用El表达式进行获取,

在此又犯了一个错误:在前端页面没有获取到值,这些list是空的,后来发现在配置result的时候,使用了重定向,而重定向是不能够共享数据的

另外:在使用模型驱动的时候,在把当前类添加到Action中,要节点new出来对象。

删除客户模块

根据id进行删除,因为这些都是新增的,所以之前的项目使用的是使用on绑定事件,现在可以使用js并且里面传参进行。传参的时候,里面的参数要加上单引号,这样当参数是字符串和数字时都支持。

function deleteCust(custId){
        if(confirm("确认删除吗?")){
            location.href="${pageContext.request.contextPath }/customerAction_delete?custId="+custId;
        }
    }

这里传的是custId,在Action中根据customer删除,此时的customer只有custId的值

@Action("customerAction_delete")
    public String delete(){
        customerService.delete(customer);
        return "findAll";
    }

修改模块出现该异常: java.lang.IllegalArgumentException: id to load is required for loading

原因是在dao 层,使用get方法,其没有获取到id。
前端传id的时候出错

<a href="${pageContext.request.contextPath }/customerAction_editUI?custId=${tempCustomer.custId}">修改</a>

修改的时候,让下拉框的值与要修改的对象查出的值是相同的,使用js进行控制。

$(function(){
        $("#custLevel option[value='${tempCustomer.baseDictLevel.dictId }']").prop("selected",true);
        $("#custSource option[value='${tempCustomer.baseDictSource.dictId }']").prop("selected",true);
        $("#custIndustry option[value='${tempCustomer.baseDictIndustry.dictId }']").prop("selected",true);
    })

但是在条件查询的时候,进行回显数据时,因为数据使用的是模型驱动进行封装,所以,直接使用Customer的属性名即可,如果属性名是对象,需要继续用里面的数据,继续点(.)就可以了

多对一以及一对多

一对多,一般会在一方放置多方的实体集合对象,
多对一,一般会在对方放置一方的实体对象。
linkman

    //描述多对一
    @ManyToOne(targetEntity=Customer.class)
    @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
    private Customer customer;
    在表单进行提交的时候,name属性提交的是customer.custId
    对象找到外键,然后根据外键把值给该列

小技巧

在使用js时,可以先使用弹出框,看当前js是否可用,以防止后面出现js没有效果,难以找到问题

前端页面不显示查到的信息

因为使用了模型驱动,所以以为可以直接返回book,前端进行获取就可以了,发现如果查询条件里没有使用book,是可以在前端页面获取的到,但是查询的时候使用了book,返回的时候就需要使用别的名字才可以在前端获取的到

book= bookService.findBookById(book);
//更改后:
Book tempbook = bookService.findBookById(book);
ActionContext.getContext().getValueStack().set("tempbook", tempbook);       

直接删除客户的时候,要级联删除联系人

需要在一对多的一方配置cascade=CascadeType.REMOVE,这个时候进行删除的时候需要先获取到要删除的客户,然后再进行将客户删除,此时可以级联删除下面的联系人。

使用下面的方式也能将该单选框选中

<input type="radio" value="1" ${tempLinkman.lkmGender=="1" ? "checked" : "" } name="lkmGender" >男

设置分页时注意问题

需要将查询总记录数放在前面,将查询分页数据放在后面,理由:如果查询分页数据,会将之前传入到限定参数自动封装进去,并且拆分不出来。而先查询总记录数时,使用投影查询,对于投影查询是可以自己设置的,涉及到分页查询数据时,只需要将投影查询设置为null即可,会自动覆盖之前的条件。

PageBean<Linkman> pageBean = new PageBean<>(pageNum,pageSize);
        //查询总记录数
        //使用投影查询
        detachedCriteria.setProjection(Projections.rowCount());
        int totalRecord = linkmanDao.findRecord(detachedCriteria);
        //查询记录数据
        //投影查询的条件可以清除掉,但是对于传入的分页限制参数不能分离,所以把查询总记录数放在上面
        detachedCriteria.setProjection(null);
        List<Linkman> lkmList = linkmanDao.findData(detachedCriteria,pageBean.getStartIndex(),pageSize);
        pageBean.setTotalRecord(totalRecord);
        pageBean.setData(lkmList);
        return pageBean;

这里有一个问题,之前写的是在下一页或者上一页的时候,直接请求指定的Action,但是这样如果是条件查询得到的结果,会把查询条件丢失,所以此时需要点击上一页或者下一页的时候,把查询条件的表单进行提交,此时每次查询都是根据条件来进行的。
这里存在一个问题,就是因为是提交表单,通过隐藏域的方式将当前页的值进行提交过去,此时如果点击到第二页,再进行条件查询的时候,会把当前页是第二页这个信息提交过去,所以在页面加载的时候,把这个隐藏域的value值清空。

$("#pageNumber").val("");

当进行下一页的时候,出现HTTP Status 404 - No result defined for action cn.itcast.ssh.action.BookAction and result input

原来是因为提交的是查询的表单,而在select中的option中,有一个“请选择的选项”,但是我没有写value属性,所以报了这个错误,此时应该写上value属性,让其值为空

<select  name="category.categoryId" style="width:20">
      <option value="">--请选择--</option>
   <c:forEach var="tempCategory" items="${categoryList }">                         
      <option ${category.categoryId==tempCategory.categoryId?"selected":"" }  value="${tempCategory.categoryId }">${tempCategory.categoryName }</option>
   </c:forEach>
</select>

在使用JSONArray进行字符串转换的时候,报没有session的错误

这是因为hibernate的懒加载问题,当根据客户的id去查找其下面的联系人的时候,没有马上查询联系人表中的customer,事务在Service层响应回来结束,在Action层进行Json转换的时候,需要将linkman表中的所有数据都转换为json,此时hibernate会再次去访问数据库查询customer属性的具体信息,但是此时session已经关闭了。

当选中客户时,自动展现对应的联系人,此时使用Ajax,在Action中将json数据响应回去的时候,因为是自己手动响应回去,此时会出现乱码问题,所以要手动解决乱码问题。

@Action("VisitAction_showLkm")
    public String showLkm() throws IOException{
        List<Linkman> linkmans = linkmanService.findBycustId(custId);
        JsonConfig jsonConfig = new JsonConfig();
        jsonConfig.setExcludes(new String[]{"customer","visits"});
        String json = JSONArray.fromObject(linkmans, jsonConfig).toString();
        ServletActionContext.getResponse().setHeader("content-type", "text/html;charset=utf-8");
        ServletActionContext.getResponse().getWriter().print(json);
        return NONE;
    }
function selectLkm(custIdEle){
        $("#linkmaninfo").html(" ");
        $.post("${pageContext.request.contextPath}/VisitAction_showLkm",{"custId":custIdEle.value},function(data){
            $(data).each(function(){
                //函数中的每一个this都代表一个linkman对象
                $("#linkmaninfo").append("<option value='"+this.lkmId+"'>"+this.lkmName+"</option>");
            });
        },"json");
    }

使用js进行 添加信息的时候,一定要记得把之前的信息清空。

属性驱动的时候,需要根据需要添加set,get方法,set方法是将页面传递的值设置进来,get方法是为了回显数据

textare文本框没有value属性,所以设置值的时候就和普通的div一样,直接在标签的中间写就可以了。

<textarea name="visitDetail" rows="5" cols="25">${currVisit.visitDetail }</textarea>                               

权限设置

首先在xml中进行配置,并且创建自己自定义的拦截器

public class AuthInterceptor extends MethodFilterInterceptor {

    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        //如果session中有user对象,就放行,没有就拦截。
        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("user");
        if(user!=null){
            invocation.invoke();
        }
        return "loginJsp";
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
//在Action中要使用以下的包名
    <package name="crm" extends="struts-default" namespace="/">

        <interceptors>
        <!-- 自定义一个拦截器 -->
            <interceptor name="myInterceptor" class="cn.itcast.web.action.AuthInterceptor"></interceptor>
            //这个是自己自定义的拦截器栈,覆盖了之前的,所以里面要手动加上之前的
            <interceptor-stack name="myStack">
                <interceptor-ref name="myInterceptor">
                    <param name="excludeMethods">login,save</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="myStack"></default-interceptor-ref>
        <global-results>
            <result name="loginJsp" type="redirect">/login.jsp</result>
        </global-results>
    </package>
</struts>   

这里是配置了自己的拦截器栈,而且包的名字也已经改变,所以在Action中不能够直接使用之前默认的包名了,需要使用现在配置的包名

对于条件判断

int pageSize=2;
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Book.class);      
        //判断用户名
        if(!StringUtils.isBlank(book.getBookName())){
            detachedCriteria.add(Restrictions.like("bookName", "%"+book.getBookName()+"%"));
        }
        if(book.getCategory()!=null && book.getCategory().getCategoryId()!=null){
            detachedCriteria.add(Restrictions.eq("category.categoryId", book.getCategory().getCategoryId()));
            System.out.println(book.getCategory().getCategoryId());
        }
        PageBean<Book> pageBean = bookService.findAllBook(detachedCriteria,pageNum,pageSize);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值