Spring的高级用法

一、Mapping的使用
Mapping是Spring的映射,可以映射到JSP代码与Java方法或类的调用,如在上传文件中,页面Form中的配置应该是这样:

<form:form name="addByUploadFile" action="${ctx}/tracerecorddetails/addByUploadFile.do" method="post" enctype="multipart/form-data" modelAttribute="traceRecordDetails">

<div ><label>导入文件:</label><input type="file" name="file" id="file" style="width: 300px;"/></div>

<button id="submitButtonId" onclick="getReferenceForm(this).submit();" class="btn3" >设置</button>

</form:form>


需要注意的是Form标签里不能嵌套Form标签,若如果需要有两个功能的同步,如:1.需要将信息保存到数据库;2.同时上传文件需要到指定文件目录。这两个操作需要在同一个页面里完成,当然可以在页面里分开来写,但是要注意的是submit()是把整个页面提交,同时刷新页面,一般要实现两个操作,最好在逻辑代码中实现。

Mapping的方法如下:

@RequestMapping(value="/addByUploadFile", method = RequestMethod.POST)
public String addByUploadFile(@RequestParam("file") MultipartFile file,TraceRecordDetails traceRecordDetails,BindingResult errors, HttpServletRequest request, ModelMap model){}

注意的是value="/yourMappingFuntion";

二、Spring中数据库的操作,如insert操作:
1.在dao中编写insert方法
2.在daoImpl中实现insert功能,这里一般是继承一个公共数据库操作的类,此处叫:BaseSpringJdbcDao,由名称就可以知道,这是一个基类,供数据库操作使用,在里面的insert方法如下:

/**
* 适用sqlserver:identity,mysql:auto_increment 自动生成主键
* KeyHolder:接口用于检索键,通常用于自动生成的键作为潜在的JDBC的插入语句返回
*/
public int insertWithGeneratedKey(Object entity, String insertSql) {
KeyHolder keyHolder = new GeneratedKeyHolder();
int affectedRows = getNamedParameterJdbcTemplate().update(insertSql, new BeanPropertySqlParameterSource(entity) , keyHolder);
setIdentifierProperty(entity, keyHolder.getKey().longValue());
return affectedRows;
}
/**
* 设置实体的主键值
*/
public void setIdentifierProperty(Object entity, Object id) {
try {
BeanUtils.setProperty(entity, getIdentifierPropertyName(), id);
} catch (Exception e) {
throw new IllegalStateException("cannot set property value:"+id+" on entityClass:"+entity.getClass()+" by propertyName:"+getIdentifierPropertyName(),e);
}
}

这个insert方法的用处在于,数据库的主键是自递增的,通过调用该方法可以返回一个实体类,这个实体类就是你需要保存数据的类,你可以在其他模块里面调用实体类的属性,通过javaBean。

三、Spring实现文件上传
通过使用MultipartFile类实现,首先介绍一下MultipartFile类:MultipartFile是一个结合Jsp用来上传文件的功能类,实现文件上传相对容易。
1.在JSP中定义JSTL中,定义下面标签,一般是在form中定义:

<form:form name="addByUploadFile" action="${ctx}/tracerecorddetails/addByUploadFile.do" method="post" enctype="multipart/form-data" modelAttribute="traceRecordDetails">

<div ><label>导入文件:</label><input type="file" name="file" id="file" style="width: 300px;"/></div>

<tr align="center" valign="middle">
<td height="60" colspan="2"><input type="submit" ID="BtnOK" value="确认上传"></td>
</tr>

action是需要映射到的实体类,method="post"意思是在action映射的类中返回一个页面,也就是post,当然还有是get,enctype="multipart/form-data"就是定义了上传文件的表示,在div中定义导入文件,这个就不说了。

2.值得注意的是,在上传文件的时候一般有保存指定路径的需求。路径是一个比较容易弄错的概念,例如,我们保存的路径是:/data/rsync/trace_passport/,由于该目录不一定存在,所以在点击上传button的时候,一般会出现目录error的问题,这个需要我们编写一个完整的目录,使用File类中的mkdir,值得注意的是,我们不能用这样:
// 创建目录
File dir = new File(path);
if (!dir.exists()) {
dir.mkdir();
}
这是因为这个只是会生成data目录,这是因为没有生成父目录,当出现这样的情况的时候需要使用一下这种方法:new File(filepath).getParentFile().mkdirs();
这样子就父目录和子目录都生成了。

3.上面已经写好了Jsp代码,我们要写的逻辑代码如下:

@RequestMapping(value="/addByUploadFile", method = RequestMethod.POST)
public String addByUploadFile(@RequestParam("file") MultipartFile file,BindingResult errors, HttpServletRequest request, ModelMap model) throws Exception{

LOGGER.info("addByUploadFile:"+ file.getOriginalFilename());
Assert.notNull(file);
Assert.isTrue(!file.isEmpty());
String filepath = "/data/rsync/trace_passport/" + savedDetails.getTraceId() + "/"+System.currentTimeMillis()+".txt";
new File(filepath).getParentFile().mkdirs();

DataOutputStream out = new DataOutputStream(new FileOutputStream(filepath));
InputStream is = null; //输入流
try {
is = file.getInputStream();
IOUtils.copy(is, out);
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(out);
}

LOGGER.info("upload file save in path:" + filepath);
return "redirect:/tracerecorddetails/enterUploadFile.do";
}

redirect是提交完毕后返回进来时候的页面。
上面的代码有几个值得关注的点:
1)使用Assert判断某些类是不是空,这样可以在调试的时候省去很多工作。
2)在IO流操作的时候有很好的技巧,有写开发人员喜欢使用byte操作读写,其实在IOUtils类中给了我们很多很方便的方法,如:复制输入输出流、自动检测流是否为空等。IOUtils.closeQuietly(is);
IOUtils.closeQuietly(out);
这里就是自动判断了是否为空,流是否已经关闭,这样就不用写一堆的if语句了。

四、SQL语句技巧
由于本人主要是做数据分析,所以对工作最多的是多表操作,下面介绍两个比较常用的SQL语句用法,一种是join,另外一种就是case when。

Join操作:

left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
举例如下:
--------------------------------------------
表A记录如下:
aID     aNum
1     a20050111
2     a20050112
3     a20050113
4     a20050114
5     a20050115
表B记录如下:
bID     bName
1     2006032401
2     2006032402
3     2006032403
4     2006032404
8     2006032408
--------------------------------------------
1.left join
sql语句如下:
 
select * from A
left join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
5     a20050115    NULL     NULL
(所影响的行数为 5 行)
结果说明:
left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的.
换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为: A.aID = B.bID).
B表记录不足的地方均为NULL.
--------------------------------------------
2.right join
sql语句如下:

select * from A
right join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
NULL     NULL     8     2006032408
(所影响的行数为 5 行)
结果说明:
仔细观察一下,就会发现,和left join的结果刚好相反,这次是以右表(B)为基础的,A表不足的地方用NULL填充.
--------------------------------------------
3.inner join
sql语句如下:

select * from A
innerjoin B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
结果说明:
很明显,这里只显示出了 A.aID = B.bID的记录.这说明inner join并不以谁为基础,它只显示符合条件的记录.
--------------------------------------------
注:
LEFT JOIN操作用于在任何的 FROM 子句中,组合来源表的记录。使用 LEFT JOIN 运算来创建一个左边外部联接。左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即使在第二个(右边)表中并没有相符值的记录。
语法:FROM table1 LEFT JOIN table2 ON table1.field1 compopr table2.field2
说明:table1, table2参数用于指定要将记录组合的表的名称。
field1, field2参数指定被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的名称。
compopr参数指定关系比较运算符:"=", "<", ">", "<=", ">=" 或 "<>"。
如果在INNER JOIN操作中要联接包含Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误.

case when的用法
1.case when最大的用处就是行列转换,下面介绍case when的用法:

Case具有两种格式。简单Case函数和Case搜索函数。
--简单Case函数

CASE sex
WHEN '1' THEN '男'
WHEN '2' THEN '女'
ELSE '其他' END

--Case搜索函数

CASE WHEN sex = '1' THEN '男'
WHEN sex = '2' THEN '女'
ELSE '其他' END


这两种方式,可以实现相同的功能。简单Case函数的写法相对比较简洁,但是和Case搜索函数相比,功能方面会有些限制,比如写判断式。
还有一个需要注意的问题,Case函数只返回第一个符合条件的值,剩下的Case部分将会被自动忽略。
如:
有一张表,里面有3个字段:语文,数学,英语。其中有3条记录分别表示语文70分,数学80分,英语58分,请用一条sql语句查询出这三条记录并按以下条件显示出来(并写出您的思路):
大于或等于80表示优秀,大于或等于60表示及格,小于60分表示不及格。
显示格式:
语文 数学 英语
及格 优秀 不及格

select
(case when 语文>=80 then '优秀'
when 语文>=60 then '及格'
else '不及格') as 语文,
(case when 数学>=80 then '优秀'
when 数学>=60 then '及格'
else '不及格') as 数学,
(case when 英语>=80 then '优秀'
when 英语>=60 then '及格'
else '不及格') as 英语,
from table xxx;


CASE 可能是 SQL 中被误用最多的关键字之一。虽然你可能以前用过这个关键字来创建字段,但是它还具有更多用法。例如,你可以在 WHERE 子句中使用 CASE。
首先让我们看一下 CASE 的语法。在一般的 SELECT 中,其语法如下:

SELECT <myColumnSpec> =
CASE
WHEN <A> THEN <somethingA>
WHEN <B> THEN <somethingB>
ELSE <somethingE>
END

在上面的代码中需要用具体的参数代替尖括号中的内容。下面是一个简单的例子:

USE pubs
GO
SELECT
Title,
'Price Range' =
CASE
WHEN price IS NULL THEN 'Unpriced'
WHEN price < 10 THEN 'Bargain'
WHEN price BETWEEN 10 and 20 THEN 'Average'
ELSE 'Gift to impress relatives'
END
FROM titles
ORDER BY price
GO

这是 CASE 的典型用法,但是使用 CASE 其实可以做更多的事情。比方说下面的 GROUP BY 子句中的 CASE:

SELECT 'Number of Titles', Count(*)
FROM titles
GROUP BY
CASE
WHEN price IS NULL THEN 'Unpriced'
WHEN price < 10 THEN 'Bargain'
WHEN price BETWEEN 10 and 20 THEN 'Average'
ELSE 'Gift to impress relatives'
END
GO

你甚至还可以组合这些选项,添加一个 ORDER BY 子句,如下所示:

USE pubs
GO
SELECT
CASE
WHEN price IS NULL THEN 'Unpriced'
WHEN price < 10 THEN 'Bargain'
WHEN price BETWEEN 10 and 20 THEN 'Average'
ELSE 'Gift to impress relatives'
END AS Range,
Title
FROM titles
GROUP BY
CASE
WHEN price IS NULL THEN 'Unpriced'
WHEN price < 10 THEN 'Bargain'
WHEN price BETWEEN 10 and 20 THEN 'Average'
ELSE 'Gift to impress relatives'
END,
Title
ORDER BY
CASE
WHEN price IS NULL THEN 'Unpriced'
WHEN price < 10 THEN 'Bargain'
WHEN price BETWEEN 10 and 20 THEN 'Average'
ELSE 'Gift to impress relatives'
END,
Title
GO

注意,为了在 GROUP BY 块中使用 CASE,查询语句需要在 GROUP BY 块中重复 SELECT 块中的 CASE 块。
除了选择自定义字段之外,在很多情况下 CASE 都非常有用。再深入一步,你还可以得到你以前认为不可能得到的分组排序结果集。
语法:

CASE expr WHEN comparison_expr1 THEN return_expr1
[WHEN comparison_expr2 THEN return_expr2
WHEN comparison_exprn THEN return_exprn
ELSE else_expr]
END


在简单的CASE表达式中,ORACLE会搜索expr 等于comparison_expr的第一对when then,然后返回return_expr.如果没有满足条件的when then 并且存在ELSE子句,那么ORACLE会返回else_expr,否则会返回空值。
您不能为所有这些return_expr和else_expr指定文字的NULL。


但我试了一下,


Id AGE
- ----------
1 20
1 22
2 10
2 200
3 30
3 40


SQL> select id,age,case id when '1' then 'aaa' when '2' then 'bbb' else NULL end from test;

结果
I AGE CAS
- ---------- ---
1 20 aaa
1 22 aaa
2 10 bbb
2 200 bbb
3 30
3 40
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值