需求:
1. 最终信息汇总示例图
2. 上图中点击新增,到下拉框1 和 2 选择后跳转到下图红色或蓝色等不同页面录入不同信息保存后回到图1汇总
需求说明
- 下拉框1号选择不同,下拉框2号里的内容不同(二级联动,此处用数据库配置,因为可增可减)
- 当第二个下拉框选择完后会下一步跳转页面填写详情(如图中红色页,蓝色页)
- 填写完成后回到图一汇总,还可以继续新增,也可以修改删除查看详情
实现思路
- 图一界面为MainObject.java 主对象对应mainForm.jsp页面,红色、蓝色等具体细节描述页面实体为RedObject.java对应redForm.jsp、BlueObject.java对应blueForm.jsp…,主对象中的各色汇总表,用一张中间表连接 称为 ConnectionObject.java对应mainForm.jsp中的List集合table表格数据,配置表configuration_table中配置2个下拉框共同决定出是哪个对象需要跳转到哪个路径
- 如何在二级联动后找到对应的实体类对象和如何跳转到它的jsp页面(路径从何而来)呢?采用了数据库中一张配置表,根据下拉框1和下拉框2的选择作为搜索条件查询出这条数据,次数据中涵盖了实体类的名称,和jsp的跳转路径,即可。
- 经过一番周折后,当各个细节实体保存数据后回到主界面时,主界面的其他一切信息是否还都存在?如何带着主对象的全部周折?采用了暂存方式,即每次需要去周折一番之前先将已有数据暂时存到数据库(数据库中数据用一个字段标志为暂存还是真的保存),回来时查询出来,最终保存时,将暂存状态改为真实持久化状态(更改标识),如果不保存,则删除数据。
实现步骤
数据库建表
- 配置表
-- Create table 配置表configuration_table中配置2个下拉框共同决定出是哪个对象需要跳转到哪个路径
create table configuration_table
(
id VARCHAR2(32) not null,
type VARCHAR2(32),
kind VARCHAR2(64),
kind_name VARCHAR2(128),
type_name VARCHAR2(128),
mapping_class VARCHAR2(128),
mapping_url VARCHAR2(128),
base_url VARCHAR2(256),
mapping_table VARCHAR2(256)
)
comment on table configuration_table
is '配置实体路径映射';
-- Add comments to the columns
comment on column configuration_table.id
is 'id';
comment on column configuration_table.type
is '下拉框1';
comment on column configuration_table.kind
is '下拉框2';
comment on column configuration_table.kind_name
is '下拉框1的名称';
comment on column configuration_table.type_name
is '下拉框2的名称';
comment on column configuration_table.mapping_class
is '对应的实体类的全路径';
comment on column configuration_table.mapping_url
is '各个细节实体controller层@RequestMapping里的路径';
comment on column configuration_table.base_url
is '需要跳转的该实体类的前端页面的url路径';
comment on column configuration_table.mapping_table
is '对应的实体类的数据库表名';
- 中间表
中间表中添加能查询到主表主对象的必要条件,和需要展示的那些汇总的共有信息。还有下拉框的信息,作为连接。 - 主表(不多介绍)
- 各个细节表(不多介绍)
实体类
- 配置表
@Data
public class configurationTable{
private static final long serialVersionUID = 1L;
private String id; // id
private String type; //
private String kind; //
private String kindName; // 名称
private String typeName; // 名称
private String mappingClass; // 对应的实体类全路径
// 对应的实体类全路径:例如:com.xxx.yyy.nnn.yyyy.sonmthing.entity.RedObject
private String mappingUrl; // 需要跳转的链接
// 需要跳转的链接 /sonmthing/redObject(就是springmvc里Controller层里
//@RequestMapping(value = "${xxxx}/sonmthing/redObject"))
private String baseUrl;// controller路径:/sonmthing/redObject(就是springmvc里Controller层里@RequestMapping路径
private String mappingTable; // 各个细节表的数据库里的表名
}
- 其他实体类就是各字段,不多介绍了
按功能划分的代码
点击图一“新增”进入下拉框页
- 前端JS:首先暂存,然后跳转
function toAdd(){
//校验一些信息是否存在
......
//异步提交暂存
$.ajax({
url: 'xxx/xxxx/xxxx/temporaryStorage',//暂存方法走一下
type: "POST",
data:$("#inputForm").serialize(),
dataType: "json",
success: function (response) {
if(response.status=='200'){
layer.alert("即将跳转下拉框页面",{
closeBtn:0,
btn:["确定"],
yes:function(mainObjectId){
var mainObjectId= response.mainObjectId;//主对象id可以带着
window.location.href="xxx/xxxx/xxxx/stagingJump?mainObjectId=" + mainObjectId;//暂存之后跳转
}
})
}else{
layer.alert("跳转失败");
}
}
});
}
- 暂存的方法 (java后端代码)
@ResponseBody
@RequestMapping(value = "temporaryStorage",method = RequestMethod.POST)
public Map<String,String> temporaryStorage(MainObject mainObject, Model model,RedirectAttributes redirectAttributes) {
String mainObjectId = mainObject.getMainObjectId();
//保存
if(StringUtils.isEmpty(mainObjectId) ) {//是空的,建个唯一id
mainObjectId = "FA"+ String.valueOf(new Date().getTime());
mainObject.setId(mainObjectId);
mainObject.setTempState(TempStatusEnum.TEMPSTATUS.getCode());//此处使用枚举标识是暂存状态
}
mainObjectService.save(mainObject);
Map<String,String> map=new HashMap<>();
map.put("status", "200");
map.put("mainObjectId", mainObjectId);
return map;
}
- 跳转下拉框(java后端代码) 把下拉框所在页用中间表ConnectionObject承载(实际并无大用)
@RequestMapping(value = "stagingJump")
public String stagingJump(MainObject mainObject, HttpServletRequest request, Model model) {
String mainObjectId = request.getParameter("mainObjectId");
ConnectionObject connectionObject = new ConnectionObject();
connectionObject .setMainObjectId(mainObjectId);
model.addAttribute("connectionObject",connectionObject);
return "xxx/xxx/xxx/connectionObjectForm";//带着主对象的id去到了中间表
}
- 下拉框的js
//静态去掉样式
<form:hidden path="id" />
<form:hidden path="mainObjectId" />
<div>
<div>
//配置在数据字典中
<span>类别:</span>
<form:select path="kind"onchange = "getType()">
<form:option value="" label="请选择类别"/>
<form:options items="${fns:getDictList('type')}" itemLabel="label" itemValue="value" htmlEscape="false"/>
</form:select>
</div>
<div>
//配置在数据库中
<span>名称:</span>
<form:select path="type">
<form:option value="" label="请选择名称"/>
</form:select>
</div>
</div>
//js
<script type="text/javascript">
function getType(){
$("#type").empty();
$("#type").prev().find(".select2-chosen").html("请选择");
$("#type").append("<option value=''>请选择</option>");
var kind = $("#kind").val();
$.ajax({
url: 'xxx/xxxx/xxxxxx/getTypeList',
type: "POST",
data: {'kind': kind},
success: function (data) {
var str="<option value=''>请选择</option>";
for(var i=0;i<data.length;i++){
str+="<option value='"+data[i].value+"'>"+data[i].label+"</option>"; //下方SQL语句即可知道
}
$("#type").empty().append(str);
}
});
}
</script>
- 下拉框的java后端代码(省略service,dao层代码)
@RequestMapping(value="getTypeList",method=RequestMethod.POST)
@ResponseBody
public List<ConfigurationTable> getTypeList(HttpServletRequest request, Model model){
String kind = request.getParameter("kind");
List<ConfigurationTable> typeList = configurationTableService.getTypeListByKind(kind);//SQL在下
return typeList;
}
对应的SQL(mybatis里)
<select id="getTypeListByKind" resultType="xxx.xxxx.xxxx.entity.ConfigurationTable">
SELECT
a.type as "value",
a.name as "label"
FROM configuration_table a
<include refid="...."/>
<where>
<if test="kind != null and kind != ''" >
a.kind = #{kind}
</if>
</where>
ORDER BY a.update_date DESC
</select>
点击图二“下一步”进入下具体实体页面
- 选择完类型 和具体实物,点击下一步
function nextPage(){
var kind = $("#kind").val();//获取选中的类
var type = $("#type").val();//获取选中的具体实物
var mainObjectId = $("#mainObjectId").val();//获取主表id
if(insuranceKind==''||insuranceType==''){
layer.alert("请先选择....");
return;
}
window.location.href="xxx/xxx/xxx/jumpPage?kind="+kind+"&type="+type+"&mainObjectId="+mainObjectId;
}
划重点反射获取划重点
@RequestMapping(value = "jumpPage")
public String jumpPage(HttpServletRequest request, Model model) {
String kind = request.getParameter("kind");
String type = request.getParameter("type");
String mainObjectId = request.getParameter("mainObjectId");
//根据下拉框一和下拉框二可以查询出唯一一条数据,这条数据中维护了对应的实体类的名字和该实体类对应页面的url路径
ConnectionObject connectionObject = connectionObjectService.getBean(kind,type);
//首先获取实体类的名字,进行反射赋值(为反射出的新鲜实体类加上必要的数据)
if(null!=connectionObject.getMappingClass()) {
//获取实体类名
String mappingClass = connectionObject.getMappingClass();
Class t = null;
Object object = null;
try {
//采用反射机制根据类的全路径名称获取实体类
t = Class.forName(mappingClass);
//实例化实体类
object = t.newInstance();
//getDeclaredFields():获得某个类的所有声明的字段
Field[] fields = t.getDeclaredFields();
for(Field f : fields) { //遍历每一个字段
//JAVA反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符。
//PUBLIC: 1 //PRIVATE: 2 //PROTECTED: 4 //STATIC: 8 //FINAL: 16 //SYNCHRONIZED: 32 //VOLATILE: 64 //TRANSIENT: 128
//NATIVE: 256 //INTERFACE: 512 //ABSTRACT: 1024 //STRICT: 2048
int modifiers=f.getModifiers();
//package java.lang.reflect;包下提供的枚举类可判断
if(!Modifier.isFinal(modifiers) && !Modifier.isStatic(modifiers) ) {
//PropertyDescriptor 类表示 JavaBean 类通过存储器导出一个属性。
/**
此类构造方法
PropertyDescriptor(String propertyName, Class<?> beanClass)-----本次采用
PropertyDescriptor(String propertyName, Class<?> beanClass, String readMethodName, String writeMethodName)
PropertyDescriptor(String propertyName, Method readMethod, Method writeMethod)
常用方法有
Class<?> getPropertyType() // 获取属性的java类型对象
Method getReadMethod()//获得用于读取属性值的方法:例如bean有个name属性,获得getName()方法
Method getWriteMethod() //获得用于写入属性值的方法:例如bean有个name属性获得setName()方法(之后会实例化并使用起来)
...
**/
PropertyDescriptor pd = new PropertyDescriptor(f.getName(), t);
Method wM = pd.getWriteMethod();//例如bean有个kind属性,获得setKind()方法=wM
//获得用于读取属性值的方法:例如bean有个name属性,此处获得getName()方法(之后会实例化并使用起来)
if("kind".equals(String.valueOf(f.getName()))) {
wM.invoke(object, kind);//实例化后获得setKind()方法可以赋值了
}
if("type".equals(String.valueOf(f.getName()))) {
wM.invoke(object, type);
}
if("mainObjectId".equals(String.valueOf(f.getName()))) {
wM.invoke(object, mainObjectId);
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
//以上是反射实例化出需要带到前端的有值的实体对象
//根据实体类全路径名获取出类名并把首字母小写(model带着到前端)
//com.xxx.yyy.nnn.yyyy.sonmthing.entity.RedObject截取出RedObject转换为redObject
int idx = mappingClass.lastIndexOf(".");//com.xxx.yyy.nnn.yyyy.sonmthing.entity.RedObject截取出RedObject
String className = mappingClass.substring(idx + 1, mappingClass.length());
className =
(new StringBuilder())
.append(Character.toLowerCase(className.charAt(0)))
.append(className.substring(1)).toString();
model.addAttribute(className, object);
//此处获取配置好的路径:例如"zz/xxxc/cvcvcvc/ccc/bsPublicObjectForm"
String mappingUrl = sysInsuredObjectMng.getMappingUrl();
//进行跳转
return mappingUrl;
}else { //此处为,如果数据库并没有配置该怎么办:用一个共用页面和实体类顶替
BsPublicObject bsPublicObject = new BsPublicObject();
bsPublicObject.setSchemeId(schemeId);
bsPublicObject.setKind(kind);
bsPublicObject.setType(type);
model.addAttribute("bsPublicObject", bsPublicObject);
return "zz/xxxc/cvcvcvc/ccc/bsPublicObjectForm";
}
}
PropertyDescriptor 类使用参考链接:https://blog.csdn.net/zhuqiuhui/article/details/78542049
具体实体页面,保存的相关事
- 提交前端省略,后端代码如下(以那个公共的为例)
@Controller
@RequestMapping(value = "${xxx}/公共/公共") //baseUrl 存的value
public class 公共Controller{
public String save(BsPublicObject bsPublicObject, Model model, RedirectAttributes redirectAttributes) {
//首先保存这个具体实物的自己的相关信息(省略)
bsPublicObjectService.save(bsPublicObject);
//其次 要把需要汇总的公共信息存入中间表中带到主表中
//主表ID获取主表对象
MainObject mainObject = mainObjectService.getMainObjectById(bsPublicObject.getMainObjectId());
//根据具体实体的Id查詢关联表中是否有记录,有则是修改,没有则是新增
ConnectionObject connectionObject= connectionObjectService.getBeanByBeanId(bsPublicObject.getId());
if(null == connectionObject) {//没有记录是新增则创建对象
connectionObject = new ConnectionObject();
//把需要汇总的公共信息存入中间表,并保存进数据库
connectionObject.setMainObjectId(bsPublicObject.getId());
...
connectionObjectService.save(connectionObject);
}
//根据主表id查询子表 里 中间表里其他的数据带到主对象页面中
List<ConnectionObject> list = mainObjectService.getConnectionListByMainObjectId(mainObject.getMainObject());
mainObject.setConnectionObjectList(list);//mainObject中有字段private List<ConnectionObject> connectionObjectList
model.addAttribute("mainObject", mainObject);
return "zzz/ddd/ffff/mainObjectForm";
}
}
主对象页面(图一)中表格:提供修改,详情,删除功能
- 前端 静态页面
<div class="panel-body">
<input id="btnAdd" class="btn btn-success" type="button" onclick="toAdd();" value="新增"/>
<table id="contentTable" class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th class="hide"></th>
<th class="hide"></th>
<th><font color="red">*</font>商品类型</th>
<th><font color="red">*</font>商品名称</th>
...
<th>操作</th>
</thead>
<tbody id="contentTableBody" >
<c:forEach items="${mainObject.connectionObjectList}" var="connectionObject">
<tr>
<td class="hide">${connectionObject.baseUrl}</td>
<td class="hide">${connectionObject.id}</td>
<td>${connectionObject.kind}</td>
<td>${connectionObject.type}</td>
<td>
<div class="..">
// baseUrl是controller路径:/sonmthing/redObject(就是springmvc里Controller层里@RequestMapping路径
<a onclick="formDetail('${connectionObject.baseUrl}','${connectionObject.id}')">详情</a>
<a onclick="form('${connectionObject.baseUrl}','${connectionObject.id}')">修改</a>
<a onclick="delete('${connectionObject.baseUrl}','${connectionObject.id}')">刪除</a>
</div>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
- 前端 js
//删除方法
function delete(url,id){
// baseUrl是controller路径:/sonmthing/redObject(就是springmvc里Controller层里@RequestMapping路径
if(!url){//如果空就去找那个公共的controller去
url = '/公共/公共';
}
var url = '${ctx}'+url+'/delete';
layer.confirm('确定删除该sss信息吗?', function(index){
$.ajax({
url: url,
type: "POST",
data:{'id':id},
dataType: "json",
success: function (data) {
if(data.state==0){
layer.confirm('删除成功',{btn:['确定'],title:"提示"},function(){
window.location.href="本页地址";
});
}else{
layer.alert("删除失败");
}
}
});
})
}
//查看详情方法
function detail(url,id){
if(!url){
url = '/公共/公共';
}
window.location.href="${xxx}"+url+"/detail?id="+id;
}
//修改方法
function form(url,id){
if(!url){
url = '/公共/公共';
}
window.location.href="${ctx}"+url+"/form?id="+id;
}
10.对应的后端
@Controller
@RequestMapping(value = "${xxx}/公共/公共") //baseUrl 存的value
public class 公共Controller{
//
@RequestMapping(value = "form")
public String form(公共对象 公共对象, Model model) {
公共对象 = 公共对象Service.get(公共对象.getId());//前端提交的id
model.addAttribute("公共对象", 公共对象);
return "zz/deeee/beeeee/bwww/公共对象Form";
}
@RequestMapping(value = "detail")
public String detail(公共对象 公共对象, Model model) {
公共对象 = 公共对象Service.get(公共对象.getId());//前端提交的id
model.addAttribute("公共对象", 公共对象);
return "zz/deeee/beeeee/bwww/公共对象Detail页";
}
@RequestMapping(value = "delete")
@ResponseBody
public Map<String,String> delete(公共对象 公共对象, Model model,RedirectAttributes redirectAttributes) {
//结果集合
Map<String,String> result = new HashMap<String,String>;
//取数据库里数据,按id
公共对象 = 公共对象Service.get(公共对象.getId());//前端提交的id
//取公共对象里存好的主对象id
String mainObjectId = bsSubBusinessInterruption.getMainObjectId ();
//删除对象本身
公共对象Service.delete(公共对象.getId());
//删除关联表
connectionObjectService.deleteByItemId(公共对象.getId());
result.put("status","200");
result.put("mainObjectId ",mainObjectId);
return result;
}
}