0%

顾问是spring提供的另一种切面,其可以完成更为负载的切面织入功能。
pointAdvisor是顾问的一种,可以指定具体的切入点。顾问将通知进行包装,会根据不同的通知类型,在不同的时间点,将切面织入不同的切入点。
pointAdvisor接口有两个常用的实现类:
1.名称匹配方法切入点顾问 NameMatchMethodPointcutAdvisor
2.正则表达式方法切入点顾问

列子1:
1.名称匹配方法切入点顾问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!– 注册目标对象 –>
<bean id=“ISomeService” class=“com.bean.service5.someServiceImpl”></bean>
<!– 注册切面:通知 –>
<bean id=“myadvice” class=“com.bean.service5.mymethodBeforeAdvice”/>
<!– 注册切面:顾问 –>
<bean id=“myAdvisor” class=“org.springframework.aop.support.NameMatchMethodPointcutAdvisor”>
<property name=“advice” ref=“myadvice”></property>
<property name=“mappedNames” value=“*ir*===方法的名字”></property>
</bean>
<!– 生成代理对象 –>
<bean id=“serviceProxy” class=“org.springframework.aop.framework.ProxyFactoryBean”>
<!– 指定目标对象 –>
<property name=“target” ref=“ISomeService”></property>
<!– 指定切面–>
<property name=“interceptorNames” value=“myAdvisor”></property>
</bean>

2结果:可以随意的指定切面通知

例子2:
正则表达式方法切入点顾问

1
2
3
4
5
<bean id=“myAdvisor” class=“org.springframework.aop.support.RegexpMethodPointcutAdvisor“>
<property name=“advice” ref=“myadvice”></property>
<!– 这里的正则表达式匹配的对象是全限定方法名 –>
<property name=“pattern” value=“.*doFirst”></property>
</bean>

后置通知:

实现methodBeforeAdvice接口。该接口中有一个接口after(),在目标方法之h后执行,后置通知特点:

  1. 在目标方法之后执行,
  2. 不改变目标方法的执行流程,后置通知代码不能阻止目标方法执行。
  3. 不改变目标方法执行的结果。

接口类:

1
2
3
4
public interface ISomeService {
public void doFirst();
public void doSecond();
}

实现类:

1
2
3
4
5
6
7
8
9
10
public class someServiceImpl implements ISomeService{
public void doFirst() {
// TODO Auto-generated method stub
System.out.println(“doFirst..”);
}
public void doSecond() {
// TODO Auto-generated method stub
System.out.println(“doSecond..”);
}
}

前置接口类:

1
2
3
4
5
6
public class mymethodBeforeAdvice implements AfterReturningAdvice{
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println(“后置通知方法”);
}
}

xml配置:

1
2
3
4
5
6
7
8
9
10
11
<!– 注册目标对象 –>
<bean id=“ISomeService” class=“com.bean.service5.someServiceImpl”></bean>
<!– 注册切面:通知 –>
<bean id=“myadvice” class=“com.bean.service5.mymethodBeforeAdvice”/>
<!– 生成代理对象 –>
<bean id=“serviceProxy” class=“org.springframework.aop.framework.ProxyFactoryBean”>
<!– 指定目标对象 –>
<property name=“target” ref=“ISomeService”></property>
<!– 指定切面–>
<property name=“interceptorNames” value=“myadvice”></property>
</bean>

测试类:

1
2
3
4
5
6
7
@Test
public void test5() {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“ApplicationContext.xml”);
ISomeService iSomeService=(ISomeService) applicationContext.getBean(“serviceProxy”);
iSomeService.doFirst();
((ClassPathXmlApplicationContext)applicationContext).close();
}

1:jar包

1
2
3
4
5
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>

配置环境

1
2
3
4
5
6
7
8
9
10
11
<!– 配置自动生成代码 –>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<configurationFile>src/main/resources/conf/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>

2:配置文件generatorConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?xml version=“1.0” encoding=“UTF-8”?> 
<!DOCTYPE generatorConfiguration
PUBLIC “-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN”
“http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd”;>

<generatorConfiguration>
<!– 配置mysql 驱动jar包路径.用了绝对路径 –>
<classPathEntry location=“D:\Maven\repository\mysql\mysql-connector-java\5.1.36\mysql-connector-java-5.1.36.jar” />

<context id=“DB2Tables” targetRuntime=“MyBatis3”>

<!– 为了防止生成的代码中有很多注释,比较难看,加入下面的配置控制 –>
<commentGenerator>
<property name=“suppressAllComments” value=“true” />
<property name=“suppressDate” value=“true” />
</commentGenerator>
<!– 注释控制完毕 –>

<!– 数据库连接 –>
<jdbcConnection driverClass=“com.mysql.jdbc.Driver”
connectionURL=“jdbc:mysql://localhost:3306/db_blog”
userId=“root”
password=“root”>
</jdbcConnection>

<javaTypeResolver >
<property name=“forceBigDecimals” value=“false” />
</javaTypeResolver>

<!– 数据表对应的model 层 –>
<javaModelGenerator targetPackage=“net.zdsoft.whb.model” targetProject=“E:\workspace\Blog\src\main\java”>
<property name=“enableSubPackages” value=“true” />
<property name=“trimStrings” value=“true” />
</javaModelGenerator>

<!– sql mapper 隐射配置文件 –>
<sqlMapGenerator targetPackage=“net.zdsoft.whb.mapping” targetProject=“E:\workspace\Blog\src\main\java”>
<property name=“enableSubPackages” value=“true” />
</sqlMapGenerator>

<!– 在ibatis2 中是dao层,但在mybatis3中,其实就是mapper接口 –>
<javaClientGenerator type=“XMLMAPPER” targetPackage=“net.zdsoft.whb.dao” targetProject=“E:\workspace\Blog\src\main\java”>
<property name=“enableSubPackages” value=“true” />
</javaClientGenerator>

<!– 要对那些数据表进行生成操作,必须要有一个. –>
<table tableName=“t_blog” schema=“untodo” domainObjectName=“blog”
enableCountByExample=“false” enableUpdateByExample=“false
enableDeleteByExample=“false” enableSelectByExample=“false
selectByExampleQueryId=“false”>
</table>
<!–
<table tableName=”user” alias=”T” domainObjectName=”User”
enableCountByExample=”false” enableUpdateByExample=”false
enableDeleteByExample=”false” enableSelectByExample=”false
selectByExampleQueryId=”false”>
</table>
–>
</context>
</generatorConfiguration>

3:maven 使用 mybatis-generator:generate

前置通知:

实现methodBeforeAdvice接口。该接口中有一个接口before(),在目标方法之前执行,前置通知特点:

  1. 在目标方法之前执行,
  2. 不改变目标方法的执行流程,前置通知代码不能阻止目标方法执行。
  3. 不改变目标方法执行的结果。

列子:

接口类:

1
2
3
4
public interface ISomeService {
public void doFirst();
public void doSecond();
}

实现类:

1
2
3
4
5
6
7
8
9
10
11
public class someServiceImpl implements ISomeService{
public void doFirst() {
// TODO Auto-generated method stub
System.out.println(“doFirst..”);
}
public void doSecond() {
// TODO Auto-generated method stub
System.out.println(“doSecond..”);
}

}

前置接口类:

1
2
3
4
5
6
public class mymethodBeforeAdvice implements MethodBeforeAdvice{
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println(“前置通知方法”);
}
}

xml配置:

1
2
3
4
5
6
7
8
9
10
11
<!– 注册目标对象 –>
<bean id=“ISomeService” class=“com.bean.service5.someServiceImpl”></bean>
<!– 注册切面:通知 –>
<bean id=“myadvice” class=“com.bean.service5.mymethodBeforeAdvice”/>
<!– 生成代理对象 –>
<bean id=“serviceProxy” class=“org.springframework.aop.framework.ProxyFactoryBean”>
<!– 指定目标对象 –>
<property name=“target” ref=“ISomeService”></property>
<!– 指定切面–>
<property name=“interceptorNames” value=“myadvice”></property>
</bean>

测试类:

1
2
3
4
5
6
7
@Test
public void test5() {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“ApplicationContext.xml”);
ISomeService iSomeService=(ISomeService) applicationContext.getBean(“serviceProxy”);
iSomeService.doFirst();
((ClassPathXmlApplicationContext)applicationContext).close();
}

加Tab 快速输出html
快捷键列表(Shortcuts Cheatsheet)
我把本文出现的Sublime Text按其类型整理在这里,以便查阅。

通用(General)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
↑↓←→:上下左右移动光标,注意不是不是KJHL!
Alt:调出菜单
Ctrl + Shift + P:调出命令板(Command Palette)
Ctrl + `:调出控制台
编辑(Editing)
Ctrl + Enter:在当前行下面新增一行然后跳至该行
Ctrl + Shift + Enter:在当前行上面增加一行并跳至该行
Ctrl + ←/→:进行逐词移动
Ctrl + Shift + ←/→进行逐词选择
Ctrl + ↑/↓移动当前显示区域
Ctrl + Shift + ↑/↓移动当前行
选择(Selecting)
Ctrl + D:选择当前光标所在的词并高亮该词所有出现的位置,再次Ctrl + D选择该词出现的下一个位置,在多重选词的过程中,使用Ctrl + K进行跳过,使用Ctrl + U进行回退,使用Esc退出多重编辑
Ctrl + Shift + L:将当前选中区域打散
Ctrl + J:把当前选中区域合并为一行
Ctrl + M:在起始括号和结尾括号间切换
Ctrl + Shift + M:快速选择括号间的内容
Ctrl + Shift + J:快速选择同缩进的内容
Ctrl + Shift + Space:快速选择当前作用域(Scope)的内容
查找&替换(Finding&Replacing)
F3:跳至当前关键字下一个位置
Shift + F3:跳到当前关键字上一个位置
Alt + F3:选中当前关键字出现的所有位置
Ctrl + F/H:进行标准查找/替换,之后:
Alt + C:切换大小写敏感(Case-sensitive)模式
Alt + W:切换整字匹配(Whole matching)模式
Alt + R:切换正则匹配(Regex matching)模式
Ctrl + Shift + H:替换当前关键字
Ctrl + Alt + Enter:替换所有关键字匹配
Ctrl + Shift + F:多文件搜索&替换
跳转(Jumping)
Ctrl + P:跳转到指定文件,输入文件名后可以:
@ 符号跳转:输入@symbol跳转到symbol符号所在的位置
# 关键字跳转:输入#keyword跳转到keyword所在的位置
: 行号跳转:输入:12跳转到文件的第12行。
Ctrl + R:跳转到指定符号
Ctrl + G:跳转到指定行号
窗口(Window)
Ctrl + Shift + N:创建一个新窗口
Ctrl + N:在当前窗口创建一个新标签
Ctrl + W:关闭当前标签,当窗口内没有标签时会关闭该窗口
Ctrl + Shift + T:恢复刚刚关闭的标签
屏幕(Screen)
F11:切换普通全屏
Shift + F11:切换无干扰全屏
Alt + Shift + 2:进行左右分屏
Alt + Shift + 8:进行上下分屏
Alt + Shift + 5:进行上下左右分屏
分屏之后,使用Ctrl + 数字键跳转到指定屏,使用Ctrl + Shift + 数字键将当前屏移动到指定屏

tld,是taglib description 的缩写,其自定义标签一般用于jsp页面,tld其作用一般是在web项目中结

jstl、c标签等用于有效性判断、权限判断等方面,对前端的一些页面标签起到约束、限制的作用。

1、tld说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version=”1.0″ encoding=”UTF-8″?>
<taglib xmlns=”http://java.sun.com/xml/ns/j2ee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd”
version=”2.0″>

<tlib-version>1.0</tlib-version>
<short-name>isnull</short-name><!– 定义标签使用的短名称 –>

<!– 自定义标签的形参都是域里面传递的参数值;自定义标签基本使用在jsp页面上 –>
<function>
<description>判断传递内容是否为空</description><!– 对该标签的说明 –>
<name>hasvalue</name><!– 定义标签名,放在短标签之后 –>
<function-class>util.Tld_util</function-class><!– 标签处理域值的类路径 –>
<function-signature>boolean isnull(java.lang.String)</function-signature><!– 标签处理域值的具体的类方法 –>
<example>${isnull:hasvalue(obj1)}</example><!– 自定义标签的使用示范 ,域参数会自动传递到具体的方法里面–>
</function>
</taglib>

2:调用

1
2
3
4
5
6
7
8
9
<c:choose>
<c:when test=”${isnull:hasvalue(${tid)}”>
<p>tld的值为123</p>
</c:when>
<c:otherwise>
<p>tld的值不是123</p>
</c:otherwise>
</c:choose>
<p>获取域值:${testtld}</p>

1:insert获取非自增主键的值

1
2
userGeneratedKeys=”true”—–使用自增主键获取主键值策略
keyProperty=”id” ——指定对应的主键属性

2:insert获取非自增主键的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!–
keyProperty:查出的主键值封装给javaBean的哪个属性
order=”BEFORE”:当前sql在插入sql之前运行
AFTER:当前sql在插入sql之后运行
resultType:查出的数据的返回值类型

BEFORE运行顺序:
先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
在运行插入的sql;就可以取出id属性对应的值
AFTER运行顺序:
先运行插入的sql(从序列中取出新值作为id);
再运行selectKey查询id的sql;
–>

3:单个参数和多个参数
单个参数:mybatis不会做特殊处理,
{参数名/任意名}:取出参数值。
多个参数:mybatis会做特殊处理。
多个参数会被封装成 一个map,
key:param1…paramN,或者参数的索引也可以
value:传入的参数值
{}就是从map中获取指定的key的值;
异常:

1
2
3
org.apache.ibatis.binding.BindingException:
Parameter ‘id’ not found.
Available parameters are [1, 0, param1, param2]

操作:
方法:

1
public Employee getEmpByIdAndLastName(Integer id,String lastName);

取值:

1
#{id},#{lastName}

【命名参数】:明确指定封装参数时map的key;@Param(“id”)
多个参数会被封装成 一个map,
key:使用@Param注解指定的值
value:参数值
{指定的key}取出对应的参数值

POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值

Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的值

TO:
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象

1
2
3
4
Page{
int index;
int size;
}

4:参数#和$的区别

1
2
${直接把值放入}
#{把值放进来主要是?}

5:select 返回map,list

1
resultType=”map”,这是直接返回map,还是就是resultType=”对象”,key的值注入用@selectMap(“id”)

6:使用association定义关联的单个对象的封装规则
规则1:

1
2
3
4
5
6
7
<!–  association可以指定联合的javaBean对象
property=”dept”:指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
<association property=”dept” javaType=”com.atguigu.mybatis.bean.Department”>
<id column=”did 数据库的外键id” property=”id 此处是Department对象的属性”/>
<result column=”dept_name” property=”departmentName”/>
</association>

规则2:
使用association进行分步查询
先查出关联字段

1
2
3
4
5
6
7
8
<!– association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性 –>
<association property=”dept”
select=”com.atguigu.mybatis.dao.DepartmentMapper.getDeptById”
column=”d_id”>
</association>

7:使用collection标签定义关联的集合类型的属性封装规则

规则1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<result column=”dept_name” property=”departmentName”/>
<!–
collection定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
–>
<collection property=”emps” ofType=”com.atguigu.mybatis.bean.Employee”>
<!– 定义这个集合中元素的封装规则 –>
<id column=”eid” property=”id”/>
<result column=”last_name” property=”lastName”/>
<result column=”email” property=”email”/>
<result column=”gender” property=”gender”/>
</collection>
规则2:分段优点
<collection property=”emps”
select=”com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId”
column=”{emps.deptId(别的数组)=id(和这个对象id)}” fetchType=”lazy”></collection>

8 discriminator javaType=””></discriminator> 鉴别器
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为

1
<discriminator javaType=”string” column=”gender”></discriminator>

9:动态sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!–
• if:判断
• choose (when, otherwise):分支选择;带了break的swtich-case
如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个
• trim 字符串截取(where(封装查询条件), set(封装修改条件))
• foreach 遍历集合

<!– 后面多出的and或者or where标签不能解决
prefix=””:前缀:trim标签体中是整个字符串拼串 后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides=””:
前缀覆盖: 去掉整个字符串前面多余的字符
suffix=””:后缀
suffix给拼串后的整个字符串加一个后缀
suffixOverrides=””
后缀覆盖:去掉整个字符串后面多余的字符

–>

<!–
collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值

#{变量名}就能取出变量的值也就是当前遍历出的元素
–>

1.切面:
交叉业务逻辑,上述中的事务处理,日志处理就可以理解为切面,常用的切面有通知与顾问,实际就是对业务逻辑的一种增强。

2.织入:
织入是将切面代码插入到目标对象的过程。

3.连接点
连接点是指可以被切面织入的方法,通常业务接口中的方法如interface里的doadd(),doupdate()均为连接点。

4.切入点
切入点指切面具体织入的方法,在StudentServiceImpl类中,若调用了doadd()就是切入点,而doupdate()仅为连接点。被标记为final的方法是不能作为连接点与切入点。因为最终的是不能被修饰的,不能被增强。

5.目标对象
目标对象指要被增强的对象。即包含主业务逻辑的类的对象,比如inter的实现类xxImpl

6.通知
通知是切面的一种实现,可以完成简单织入功能,通知定义了增强代码切入到目标代码的时间点,确定是目标方法执行之前,还是之后执行等。通知类型不同,切入时间不同。

7.顾问
顾问是切面的另一种实现,能够将通知以更为复杂方式织入到目标对象中,是将通知包装成更为复杂切面的装配器。

先说一下思路吧,普通的分页大家初学java就会了,其实加载更多也非常简单。jQuery中有 $(“#Loading”).before(data); 这个before方法,可以向id为Loading的元素前追加HTML,因此只要我们的controller或者action返回HTML并追加到该元素前就行了。在默认的情况下我们发送Ajax请求,如果后台返回某个页面,那么浏览器会自动将该页面的HTML代码返回给Ajax回调函数,我们只要将此HTML用和上面的before方法追加上就OK了。(加载更多页面就是前面那个绿色的“某个页面”,这个加载更多页面我们只要将原先的页面里面的for循环部分拷贝过来放在body中),好了我该上例子了!

controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.htth.controller.api;  

import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.htth.entity.UserInfo;
import com.htth.service.CommonService;
/**
* 加载更多
* @author xuxile
*/
@Controller
@RequestMapping(“/test”)
public class TestController {

@Resource
private CommonService commonService;

/**
* 用户列表
*/
@RequestMapping(value=“/list”)
public String list(Model model,HttpServletResponse response,HttpServletRequest request) throws Exception {
String hql=“from UserInfo”;
List<UserInfo> list=commonService.findByHql(hql, 1, 5);
model.addAttribute(“list”, list);
return “user/list”;
}

/**
* 加载更多
* @param activityid 活动id
*/
@RequestMapping(value=“/more”)
public String more(Model model,int currPage,HttpServletResponse response,HttpServletRequest request) throws Exception {
String hql=“from UserInfo”;
List<UserInfo> list=commonService.findByHql(hql,currPage, 5);
model.addAttribute(“list”, list);
return “user/more”;
}

}

用户列表页面:list.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<%@ page language=“java” contentType=“text/html; charset=UTF-8” pageEncoding=“UTF-8”%>  
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core”; prefix=“c”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/fmt”; prefix=“fmt”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/functions”; prefix=“fn”%>
<%
String path = request.getContextPath(); ///silomallManager
String basePath = request.getScheme()+“://”+request.getServerName()+“:”+request.getServerPort()+path;//http://localhost:8080/silomallManager/
%>
<html>
<head>
<title>用户列表</title>
<script src=“http://www.w3school.com.cn/jquery/jquery-1.11.1.min.js”;></script>
<script>
var stop=true;
var currPage=1;
$(window).scroll(function(){
totalheight = parseFloat($(window).height()) + parseFloat($(window).scrollTop());
if($(document).height() <= totalheight){
if(stop==true){
stop=false;
currPage++;
setTimeout(function(){
//post请求开始
$.post(“<%=path%>/test/more”,
{
currPage:currPage
},
function(data){
<span style=“color:#006600;”>$(“#Loading”).before(data); </span>
stop=true;
});
//post请求结束
}, 3000);

}
}
});
</script>
</head>

<body>
<ul>
<li>加载更多测试</li>
<li>用户名 | 密码 | 注册时间 | </li>
<span style=“color:#000099;”><c:forEach var=“u” items=“${list}” varStatus=“xxl” begin=“0”>
<li>${u.loginName} | ${u.password} | ${u.buildTime} | </li>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
</c:forEach></span>
<span style=“color:#009900;”><div id=“Loading”>Loading…</div></span>
</ul>
</body>
</html>

加载更多页面:more.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page language=“java” contentType=“text/html; charset=UTF-8” pageEncoding=“UTF-8”%>  
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core”; prefix=“c”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/fmt”; prefix=“fmt”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/functions”; prefix=“fn”%>
<%
String path = request.getContextPath(); ///silomallManager
String basePath = request.getScheme()+“://”+request.getServerName()+“:”+request.getServerPort()+path;//http://localhost:8080/silomallManager/
%>
<html>
<head>
<title>加载更多</title>
</head>

<body>
<span style=“color:#000099;”><c:forEach var=“u” items=“${list}” varStatus=“xxl” begin=“0”>
<li>${u.loginName} | ${u.password} | ${u.buildTime} | </li>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
</c:forEach></span>
</body>
</html>

OK,两个页面,两个controller中的方法就完成了加载更多。不需要其他任何插件。

1:插入maven

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>

2:配置mybatis-config.xml

1
2
3
4
5
6
<plugins>
<plugin interceptor=“com.github.pagehelper.PageHelper”>
<!– 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库–>
<property name=“dialect” value=“mysql”/>
</plugin>
</plugins>

3:注入session

1
2
3
4
5
6
7
<!– springMyBatis完美整合,不需要mybatis的配置映射文件 –> 
<bean id=“sqlSessionFactory” class=“org.mybatis.spring.SqlSessionFactoryBean”>
<property name=“dataSource” ref=“dataSource” />
<!– 自动扫描mapping.xml文件 –>
<property name=“mapperLocations” value=“classpath:com/java/mapper/*.xml”></property>
<property name=“configLocation” value=“classpath:mybatis-config.xml”></property>
</bean>

4:分页

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping(“/ss”)
public String page(@RequestParam(required = false,defaultValue = “1”,value = “pn”)Integer startPage,Model model) {
int pageSize =10;
System.out.println(“startPage==”+startPage);
PageHelper.startPage(startPage, pageSize);
List<Blog> blogList=blogService.Pagelist();
PageInfo<Blog> pageInfo = new PageInfo<Blog>(blogList);
System.out.println(“共有:”+ pageInfo.getLastPage());
model.addAttribute(“pageInfo”, pageInfo);
return “page”;

}

5:page.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<%@ page language=“java” contentType=“text/html; charset=utf-8”
pageEncoding=“utf-8”%>
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core”; prefix=“c”%>
<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”;>
<html>
<head>
<meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”>
<title>Insert title here</title>
</head>
<body>
<c:forEach var=“blogTypeCount” items=“${pageInfo.list }“>
<li><span><a href=“#”>${blogTypeCount.title }</a></span></li>
</c:forEach>
<div class=“col-md-6”>
当前第 ${pageInfo.pageNum} 页.总共 ${pageInfo.pages} 页.一共 ${pageInfo.total} 条记录
</div>

<div class=“col-md-6”>
<nav aria-label=“Page navigation”>
<ul class=“pagination”>

<li><a href=“${pageContext.request.contextPath}/page.do?pn=1″>首页</a></li>

<!–上一页–>
<li>
<c:if test=“${pageInfo.hasPreviousPage}“>
<a href=“${pageContext.request.contextPath}/page.do?pn=${pageInfo.pageNum-1}“ aria-label=“Previous”>
<span aria-hidden=“true”>«</span>
</a>
</c:if>
</li>

<!–循环遍历连续显示的页面,若是当前页就高亮显示,并且没有链接–>
<c:forEach items=“${pageInfo.navigatepageNums}“ var=“page_num”>
<c:if test=“${page_num == pageInfo.pageNum}“>
<li class=“active”><a href=“#”>${page_num}</a></li>
</c:if>
<c:if test=“${page_num != pageInfo.pageNum}“>
<li><a href=“${pageContext.request.contextPath}/page.do?pn=${page_num}“>${page_num}</a></li>
</c:if>
</c:forEach>

<!–下一页–>
<li>
<c:if test=“${pageInfo.hasNextPage}“>
<a href=“${pageContext.request.contextPath}/page.do?pn=${pageInfo.pageNum+1}“
aria-label=“Next”>
<span aria-hidden=“true”>»</span>
</a>
</c:if>
</li>

<li><a href=“${pageContext.request.contextPath}/page?pn=${pageInfo.pages}“>尾页</a></li>
</ul>
</nav>
</div>


</body>
</html>

触发器是基于行触发的,所以删除、新增或者修改操作可能都会激活触发器,所以不要编写过于复杂的触发器,也不要增加过得的触发器,这样会对数据的插入、修改或者删除带来比较严重的影响,同时也会带来可移植性差的后果,所以在设计触发器的时候一定要有所考虑。
触发器是一种特殊的存储过程,它在插入,删除或修改特定表中的数据时触发执行,它比数据库本身标准的功能有更精细和更复杂的数据控制能力。
数据库触发器有以下的作用:(红色表示用过)

  1. 安全性。可以基于数据库的值使用户具有操作数据库的某种权利。
    • 可以基于时间限制用户的操作,例如不允许下班后和节假日修改数据库数据。
    • 可以基于数据库中的数据限制用户的操作,例如不允许股票的价格的升幅一次超过10%。
  2. 审计。可以跟踪用户对数据库的操作。
    • 审计用户操作数据库的语句。
    • 把用户对数据库的更新写入审计表。
  3. 实现复杂的数据完整性规则
    • 实现非标准的数据完整性检查和约束。触发器可产生比规则更为复杂的限制。与规则不同,触发器可以引用列或数据库对象。例如,触发器可回退任何企图吃进超过自己保证金的期货。
    • 提供可变的缺省值。
  4. 实现复杂的非标准的数据库相关完整性规则。触发器可以对数据库中相关的表进行连环更新。例如,在auths表author_code列上的删除触发器可导致相应删除在其它表中的与之匹配的行。
    • 在修改或删除时级联修改或删除其它表中的与之匹配的行。
    • 在修改或删除时把其它表中的与之匹配的行设成NULL值。
    • 在修改或删除时把其它表中的与之匹配的行级联设成缺省值。
    • 触发器能够拒绝或回退那些破坏相关完整性的变化,取消试图进行数据更新的事务。当插入一个与其主健不匹配的外部键时,这种触发器会起作用。例如,可以在books. author_code 列上生成一个插入触发器,如果新值与auths.author_code列中的某值不匹配时,插入被回退。
  5. 同步实时地复制表中的数据。
  6. 自动计算数据值,如果数据的值达到了一定的要求,则进行特定的处理。例如,如果公司的帐号上的资金低于5万元则立即给财务人员发送警告数据。

1
2
3
4
5
<settings>
<setting name=“cacheEnabled” value=“true”/>
<setting name=“lazyLoadingEnabled” value=“true”/>
<setting name=“aggressiveLazyLoading“ value=“true”/>
</settings>

一级缓存与二级缓存的不同:
sqlsession一旦关闭,则sqlsession中的数据将不存在,就是一级缓存不存在的。而二级缓存的生命周期会与整个应用同步,与sqlsession是否关闭无关。
一级缓存在同意线程间共享数据,而二级缓存是在不同线程间共享数据。
一级缓存就是select查出来数据,他是根据namespace和id一起确定缓存的,而hibernate就是根据id来查的,增删改清空缓存。
二级缓存:

1
<cache eviction = FIFOflushInterval =”1080000″ readOnly =”true” size=”512″>
  1. LRU – 最近最少使用的:移除最长时间不被使用的对象。

  2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

flushInterval :刷新缓存的时间间隔,一般是毫秒。

readOnle:是否只读

size:二级缓存的数量

1.一对多,一的用集合set<student>
比如教师的id,和学生的tearcherId
1.1先查询student

1
2
3
4
5
<select id=“selectUsers” resultType=“User”>
select id, username, tearcherId
from student
where id = #{id}
</select>

1.2在查询tearcher表

1
2
3
4
5
6
7
<resultMap id=“detailedBlogResultMap” type=“Blog”>
<id property=“id” column=“comment_id”/>
<result property=“username” column=“user_name”/>
<collection property=“comments” ofType=“student”>
<id property=“id” column=“comment_id”/>
</collection>
</resultMap>

改进版:

2.多对一 对的是一个对象

3.多对多 本质:基本还是一对也多,或者是多对一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id=“detailedBlogResultMap” type=“Blog”>
<constructor>
<idArg column=“blog_id” javaType=“int”/>
</constructor>
<result property=“title” column=“blog_title”/>
<association property=“author” javaType=“Author”>
<id property=“id” column=“author_id”/>
<result property=“username” column=“author_username”/>
<result property=“password” column=“author_password”/>
<result property=“email” column=“author_email”/>
<result property=“bio” column=“author_bio”/>
<result property=“favouriteSection” column=“author_favourite_section”/>
</association>
<collection property=“posts” ofType=“Post”>
<id property=“id” column=“post_id”/>
<result property=“subject” column=“post_subject”/>
<association property=“author” javaType=“Author”/>
<collection property=“comments” ofType=“Comment”>
<id property=“id” column=“comment_id”/>
</collection>
</resultMap>

aop,面向切面编程,是面向对象编程的一种补充。
aop底层,就是采用动态代理模式实现的。采用了两种代理:jdk动态代理,与CGLIB的动态代理

面向切面编程,就是将交叉业务逻辑封装成切面,利用aop容器的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的,与主业务逻辑无关的代码,如安全检查,事务,日志等。

例子:转账,在真正转账业务逻辑前后,比如转账之前,需要进行日志记录,加载事务,结束事务等交叉逻辑,而这些业务逻辑与主业务并没有直接关系,但他们的代码量很多,占了一大部分,并且会干扰程序员的逻辑思维,在处理主业务时,也处理其他事务,aop就是把他们分开,做到分开处理。

在开发WEB前端页面时,经常会根据动态的数据加载一些HTML控件,如果把这些HTML代码全写入JS中,根据不同的数据显示的控件也不一样,操作起来会显得比较繁琐,而且极不利于代码的维护,怎么办呢?现在就是我要说的,使用模板方法,直接把数据往里面套就好了。废话不多说,以示例来说明。

先写个非常重要的JS方法,此方法就是用来填充格式数据的。(看不懂也没关系,会用就行)

Js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
function formatTemplate(dta, tmpl) {  
var format = {
name: function(x) {
return x
}
};
return tmpl.replace(/{(\w+)}/g, function(m1, m2) {
if (!m2)
return “”;
return (format && format[m2]) ? format[m2](dta[m2]) : dta[m2];
});
}
`

接下来就用示例来说明:
例如:从服务器取出一个JSON串,把数据显示在一组HTML控件上,现在我先把HTML代码写下来:
Html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type=“text/template”>  
<tr mgid=“{mgid}” mid=“{mid}”>
<td>
<input type=“checkbox” mid=“{mid}”></td>
<td>
<a href=“{localfile}” data-fancybox-group=“button” class=“fancybox-buttons”>
<img src=“{localfile}” style=“width:45px;height:45px;”></a>
</td>
<td>
<input type=“text” class=“input-large validvalue=“{medianame}” onblur=“TextOnBlur(this)” onclick=“TextOnFocus(this)” name=“medianame” mid=“{mid}” readonly=“readonly”></td>
<td>
<a onclick=“updateMediaName(this)” href=“javascript:void(0);”>重命名</a>
<a onclick=“showbulkUploadTemplate(this)” name=“edit” localfile=“{localfile}” href=“javascript:void(0);”>替换</a>
<a onclick=“daleteMedia(this)” href=“javascript:void(0);”>删除</a>
<a onclick=“setMediaFaceImage(this);” title=“设置为分组【{groupname}】的封面” groupname=“{groupname}” mid=“{mid}” href=“javascript:void(0);”>设置封面</a>
</td>
</tr>
</script>

大家看到了这段代码,如果全部写JS上确实比较烦,而且大家也发现了,为什么在首尾有<script>标签,里面有{}括号括住了一些值,为什么这么写呢,别急别急。其实这些{}号中的数据,就是我们要填充的数据的地方。括号中的名称就是存贮值的变量,好,要怎么填呢?
若我们从服务器上取到的JSON如下:
Js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{  
“total”: “1”,
“page”: “1”,
“records”: “3”,
“rows”: [{
“groupname”: “美食图片”,
“mid”: 4766,
“sid”: 517,
“medianame”: “Tulips”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/0x6dvf.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673220000+0800)\/”,
“uploadtime”: “\/Date(1410673220000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 17.41
}, {
“groupname”: “美食图片”,
“mid”: 4765,
“sid”: 517,
“medianame”: “Penguins”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/6iluw6.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673215000+0800)\/”,
“uploadtime”: “\/Date(1410673215000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 15.62
}, {
“groupname”: “美食图片”,
“mid”: 4764,
“sid”: 517,
“medianame”: “Lighthouse”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/fx0kzp.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673209000+0800)\/”,
“uploadtime”: “\/Date(1410673209000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 14.2
}]
}

我们要填写到地方定义在下面Table中
Html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<html>  
<body>
<table id=“tableData”>
<tr class=“firstLine”>
<th></th>
<th>图片</th>
<th>图片名称</th>
<th>类型</th>
<th>大小</th>
<th>尺寸</th>
<th>上传日期</th>
<th>操作</th>
<th></th>
</tr>
</table>
</body>
</html>

好了准备工作做好了,重点的来了,别看走眼了:
Js代码
$.ajax({
url: ‘/manage/GetAllMediaListPage’,
type: ‘get’,
data: data,
cache: false,
dataType: “json”,
success: function(dta) {
if (!dta || !dta.rows || dta.rows.length <= 0) {
return;
}

//获取模板上的HTML
var html = $(‘script[type=”text/template”]’).html();
//定义一个数组,用来接收格式化合的数据
var arr = [];
//对数据进行遍历
$.each(dta.rows, function(i, o) {
//这里取到o就是上面rows数组中的值, formatTemplate是最开始定义的方法.
arr.push(formatTemplate(o, html));
});
//好了,最后把数组化成字符串,并添加到table中去。
$(‘#tableData’).append(arr.join(”));
//走完这一步其实就完成了,不会吧,这么简单,不错,就是这么简单!! 不信就自己动手去试试!
}
});

哟嚯,搞定,等等,好像表格的列数对不上,是吧,那是因为我没有把模板写完整出来,继续往下看。
现在我来解释为什么把模板代码放在<script></script>中间,假如,你把模板代码放在某个<div>中并隐藏起来,那么可能你的代码中会用到$(‘input[type=”text”]’)查找控件时,不好意思,就会把模板中的也统计进去了,这个并不是你想要的。所以我用<script>,这么做还有一个好处,就是不会被当成HTML来执行显示出来, 但我们也得保证不能当成js来执行,所以加了个type=”text/template”,没有这个类型的,自己明白就好了。
另外,像o.mid的数值只会填充到{mid}这个里,不会填充到别的地方去,而且{mid}可以存在多个,一并全部替换成实际数值了。

接下来的一个问题就是,我取到的数据可能并不是我要给用户显示的,那么就需要变通一下了

Js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var html = $(‘script[type=”text/template”]’).html();  
var arr = [];
$.each(dta.rows, function(i, o) {
//atime,asize和fsize这三个变量是之前的JSON中没有的,可灵活设置一下:
//格式化时间,当然getFormatDate这个函数我也公布出来了,格式化时间而已,见最后面。
o.atime = getFormatDate(o.uploadtime ? o.uploadtime : o.createtime, ‘yyyy-MM-dd’);
//图片的尺寸大小: 就是把上面的o.width和o.height变量组合一下,如果任何一个不存在,则返回”-“
o.asize = (o.width && o.height) ? o.width + ‘ * ‘ + o.height : ‘-‘;
//图片大小。存在才显示xxKB
o.fsize = o.seizespace ? o.seizespace + ‘&nbsp; KB’ : ‘-‘;
//格式化模板数据
arr.push(formatTemplate(o, html));
});
$(‘#tableData’).append(arr.join(”));

完整的HTML模板如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type=“text/template”>  
<tr mgid=“{mgid}” mid=“{mid}”>
<td>
<input type=“checkbox” mid=“{mid}”></td>
<td>
<a href=“{localfile}” data-fancybox-group=“button” class=“fancybox-buttons”>
<img src=“{localfile}” style=“width:45px;height:45px;”></a>
</td>
<td>
<input type=“text” class=“input-large validvalue=“{medianame}” onblur=“TextOnBlur(this)” onclick=“TextOnFocus(this)” name=“medianame” mid=“{mid}” readonly=“readonly”></td>
<td>{mediatype}</td>
<!– 各位看官,自定义的三个属性在这里哦~~ –>
<td>{fsize}</td>
<td>{asize}</td>
<td>{atime}</td>
<td>
<a onclick=“updateMediaName(this)” href=“javascript:void(0);”>重命名</a>
<a onclick=“showbulkUploadTemplate(this)” name=“edit” localfile=“{localfile}” href=“javascript:void(0);”>替换</a>
<a onclick=“daleteMedia(this)” href=“javascript:void(0);”>删除</a>
<a onclick=“setMediaFaceImage(this);” title=“设置为分组【{groupname}】的封面” groupname=“{groupname}” mid=“{mid}” href=“javascript:void(0);”>设置封面</a>
</td>
</tr>
</script>

其实想通了还是挺简单的,当然formatTemplate函数中的第一个参数必须要求是像struct的对象,直接一点就是这样一个数据包: { “A”: “a”, “B”: “b”, “C”:”c” } 。懂了吧伙计们,不懂就慢慢看吧,嘿嘿!

附: 格式化时间函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function getFormatDate(xdate, format) {  
try {
var format = format || ‘yyyy-MM-dd HH:mm:ss’;
var date = (xdate instanceof Date) ? xdate : new Date(parseInt(xdate.replace(‘/Date(‘, ”).replace(‘)/’, ”), 10));
var lang = {
‘M+’: date.getMonth() + 1,
‘d+’: date.getDate(),
‘H+’: date.getHours(),
‘m+’: date.getMinutes(),
‘s+’: date.getSeconds()
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + ”).substr(4RegExp.$1.length));
}
for (var key in lang) {
if (new RegExp(‘(‘ + key + ‘)’).test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ?
lang[key] : (’00’ + lang[key]).substr((” + lang[key]).length));
}
}
return format;
} catch (e) {
return ‘-‘;
}
}

在开发WEB前端页面时,经常会根据动态的数据加载一些HTML控件,如果把这些HTML代码全写入JS中,根据不同的数据显示的控件也不一样,操作起来会显得比较繁琐,而且极不利于代码的维护,怎么办呢?现在就是我要说的,使用模板方法,直接把数据往里面套就好了。废话不多说,以示例来说明。

先写个非常重要的JS方法,此方法就是用来填充格式数据的。(看不懂也没关系,会用就行)
Js代码

1
2
3
4
5
6
7
8
9
10
11
12
function formatTemplate(dta, tmpl) {  
var format = {
name: function(x) {
return x
}
};
return tmpl.replace(/{(\w+)}/g, function(m1, m2) {
if (!m2)
return “”;
return (format && format[m2]) ? format[m2](dta[m2]) : dta[m2];
});
}

接下来就用示例来说明:
例如:从服务器取出一个JSON串,把数据显示在一组HTML控件上,现在我先把HTML代码写下来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type=“text/template”>  
<tr mgid=“{mgid}” mid=“{mid}”>
<td>
<input type=“checkbox” mid=“{mid}”></td>
<td>
<a href=“{localfile}” data-fancybox-group=“button” class=“fancybox-buttons”>
<img src=“{localfile}” style=“width:45px;height:45px;”></a>
</td>
<td>
<input type=“text” class=“input-large validvalue=“{medianame}” onblur=“TextOnBlur(this)” onclick=“TextOnFocus(this)” name=“medianame” mid=“{mid}” readonly=“readonly”></td>
<td>
<a onclick=“updateMediaName(this)” href=“javascript:void(0);”>重命名</a>
<a onclick=“showbulkUploadTemplate(this)” name=“edit” localfile=“{localfile}” href=“javascript:void(0);”>替换</a>
<a onclick=“daleteMedia(this)” href=“javascript:void(0);”>删除</a>
<a onclick=“setMediaFaceImage(this);” title=“设置为分组【{groupname}】的封面” groupname=“{groupname}” mid=“{mid}” href=“javascript:void(0);”>设置封面</a>
</td>
</tr>
</script>

大家看到了这段代码,如果全部写JS上确实比较烦,而且大家也发现了,为什么在首尾有<script>标签,里面有{}括号括住了一些值,为什么这么写呢,别急别急。其实这些{}号中的数据,就是我们要填充的数据的地方。括号中的名称就是存贮值的变量,好,要怎么填呢?
若我们从服务器上取到的JSON如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{  
“total”: “1”,
“page”: “1”,
“records”: “3”,
“rows”: [{
“groupname”: “美食图片”,
“mid”: 4766,
“sid”: 517,
“medianame”: “Tulips”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/0x6dvf.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673220000+0800)\/”,
“uploadtime”: “\/Date(1410673220000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 17.41
}, {
“groupname”: “美食图片”,
“mid”: 4765,
“sid”: 517,
“medianame”: “Penguins”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/6iluw6.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673215000+0800)\/”,
“uploadtime”: “\/Date(1410673215000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 15.62
}, {
“groupname”: “美食图片”,
“mid”: 4764,
“sid”: 517,
“medianame”: “Lighthouse”,
“mgid”: 549,
“mediatype”: “image”,
“mediaid”: “”,
“timestamp”: “”,
“localfile”: “/UploadFile/image/201409/14/fx0kzp.jpg”,
“picurl”: “”,
“thumbid”: “”,
“voiceformat”: “”,
“state”: 1,
“createtime”: “\/Date(1410673209000+0800)\/”,
“uploadtime”: “\/Date(1410673209000+0800)\/”,
“width”: 480,
“height”: 360,
“seizespace”: 14.2
}]
}

我们要填写到地方定义在下面Table中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>  
<body>
<table id=“tableData”>
<tr class=“firstLine”>
<th></th>
<th>图片</th>
<th>图片名称</th>
<th>类型</th>
<th>大小</th>
<th>尺寸</th>
<th>上传日期</th>
<th>操作</th>
<th></th>
</tr>
</table>
</body>
</html>

好了准备工作做好了,重点的来了,别看走眼了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$.ajax({  
url: ‘/manage/GetAllMediaListPage’,
type: ‘get’,
data: data,
cache: false,
dataType: “json”,
success: function(dta) {
if (!dta || !dta.rows || dta.rows.length <= 0) {
return;
}

//获取模板上的HTML
var html = $(‘script[type=”text/template”]’).html();
//定义一个数组,用来接收格式化合的数据
var arr = [];
//对数据进行遍历
$.each(dta.rows, function(i, o) {
//这里取到o就是上面rows数组中的值, formatTemplate是最开始定义的方法.
arr.push(formatTemplate(o, html));
});
//好了,最后把数组化成字符串,并添加到table中去。
$(‘#tableData’).append(arr.join(”));
//走完这一步其实就完成了,不会吧,这么简单,不错,就是这么简单!! 不信就自己动手去试试!
}
});

哟嚯,搞定,等等,好像表格的列数对不上,是吧,那是因为我没有把模板写完整出来,继续往下看。
现在我来解释为什么把模板代码放在<script></script>中间,假如,你把模板代码放在某个<div>中并隐藏起来,那么可能你的代码中会用到$(‘input[type=”text”]’)查找控件时,不好意思,就会把模板中的也统计进去了,这个并不是你想要的。所以我用<script>,这么做还有一个好处,就是不会被当成HTML来执行显示出来,但我们也得保证不能当成js来执行,所以加了个type=”text/template”,没有这个类型的,自己明白就好了。

另外,像o.mid的数值只会填充到{mid}这个里,不会填充到别的地方去,而且{mid}可以存在多个,一并全部替换成实际数值了。

接下来的一个问题就是,我取到的数据可能并不是我要给用户显示的,那么就需要变通一下了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var html = $(‘script[type=”text/template”]’).html();  
var arr = [];
$.each(dta.rows, function(i, o) {
//atime,asize和fsize这三个变量是之前的JSON中没有的,可灵活设置一下:
//格式化时间,当然getFormatDate这个函数我也公布出来了,格式化时间而已,见最后面。
o.atime = getFormatDate(o.uploadtime ? o.uploadtime : o.createtime, ‘yyyy-MM-dd’);
//图片的尺寸大小: 就是把上面的o.width和o.height变量组合一下,如果任何一个不存在,则返回”-“
o.asize = (o.width && o.height) ? o.width + ‘ * ‘ + o.height : ‘-‘;
//图片大小。存在才显示xxKB
o.fsize = o.seizespace ? o.seizespace + ‘&nbsp; KB’ : ‘-‘;
//格式化模板数据
arr.push(formatTemplate(o, html));
});
$(‘#tableData’).append(arr.join(”));

完整的HTML模板如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type=“text/template”>  
<tr mgid=“{mgid}” mid=“{mid}”>
<td>
<input type=“checkbox” mid=“{mid}”></td>
<td>
<a href=“{localfile}” data-fancybox-group=“button” class=“fancybox-buttons”>
<img src=“{localfile}” style=“width:45px;height:45px;”></a>
</td>
<td>
<input type=“text” class=“input-large valid” value=“{medianame}” onblur=“TextOnBlur(this)” onclick=“TextOnFocus(this)” name=“medianame” mid=“{mid}” readonly=“readonly”></td>
<td>{mediatype}</td>
<!– 各位看官,自定义的三个属性在这里哦~~ –>
<td>{fsize}</td>
<td>{asize}</td>
<td>{atime}</td>
<td>
<a onclick=“updateMediaName(this)” href=“javascript:void(0);”>重命名</a>
<a onclick=“showbulkUploadTemplate(this)” name=“edit” localfile=“{localfile}” href=“javascript:void(0);”>替换</a>
<a onclick=“daleteMedia(this)” href=“javascript:void(0);”>删除</a>
<a onclick=“setMediaFaceImage(this);” title=“设置为分组【{groupname}】的封面” groupname=“{groupname}” mid=“{mid}” href=“javascript:void(0);”>设置封面</a>
</td>
</tr>
</script>

其实想通了还是挺简单的,当然formatTemplate函数中的第一个参数必须要求是像struct的对象,直接一点就是这样一个数据包: { “A”: “a”, “B”: “b”, “C”:”c” } 。懂了吧伙计们,不懂就慢慢看吧,嘿嘿!

附: 格式化时间函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function getFormatDate(xdate, format) {  
try {
var format = format || ‘yyyy-MM-dd HH:mm:ss’;
var date = (xdate instanceof Date) ? xdate : new Date(parseInt(xdate.replace(‘/Date(‘, ”).replace(‘)/’, ”), 10));
var lang = {
‘M+’: date.getMonth() + 1,
‘d+’: date.getDate(),
‘H+’: date.getHours(),
‘m+’: date.getMinutes(),
‘s+’: date.getSeconds()
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + ”).substr(4RegExp.$1.length));
}
for (var key in lang) {
if (new RegExp(‘(‘ + key + ‘)’).test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ?
lang[key] : (’00’ + lang[key]).substr((” + lang[key]).length));
}
}
return format;
} catch (e) {
return ‘-‘;
}
}

创建触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TRIGGER trigger_name trigger_time trigger_event ON tb_name FOR EACH ROW trigger_stmt
trigger_name:触发器的名称
tirgger_time:触发时机,为BEFORE或者AFTER
trigger_event:触发事件,为INSERTDELETE或者UPDATE
tb_name:表示建立触发器的表明,就是在哪张表上建立触发器
trigger_stmt:触发器的程序体,可以是一条SQL语句或者是用BEGINEND包含的多条语句
所以可以说MySQL创建以下六种触发器:
BEFORE INSERT
BEFORE DELETE
BEFORE UPDATE
AFTER INSERT
AFTER DELETE
AFTER UPDATE

BEFORE和AFTER参数指定了触发执行的时间,在事件之前或是之后
FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器
触发器可以是一条SQL语句,也可以是多条SQL代码块,那如何创建呢?

1
2
3
4
5
6
7
8
DELIMITER $  #将语句的分隔符改为$
BEGIN
sql1;
sql2;

sqln
END $
DELIMITER ; #将语句的分隔符改回原来的分号”;”

在BEGIN…END语句中也可以定义变量,但是只能在BEGIN…END内部使用:

1
SET s2 = “ is created”;

1.动态sql if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id=“findActiveBlogWithTitleLike”
resultType=“Blog”>
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test=“title != null”>
AND title like #{title}
</if></select>

<select id=“findActiveBlogLike”
resultType=“Blog”>
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test=“title != null”>
AND title like #{title}
</if>
<if test=“author != null and author.name != null”>
AND author_name like #{author.name}
</if></select>

2.动态sql choose, when, otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id=“findActiveBlogLike”
resultType=“Blog”>
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test=“title != null”>
AND title like #{title}
</when>
<when test=“author != null and author.name != null”>
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose></select>

3where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id=“findActiveBlogLike”
resultType=“Blog”>
SELECT * FROM BLOG
<where>
<if test=“state != null”>
state = #{state}
</if>
<if test=“title != null”>
AND title like #{title}
</if>
<if test=“author != null and author.name != null”>
AND author_name like #{author.name}
</if>
</where></select>

4.foreach

1
2
3
4
5
6
7
8
9
10
11
12
<select id=“selectPostIn” resultType=“domain.blog.Post”>
SELECT *
FROM POST P
WHERE ID in
<foreach item=“item” index=“index” collection=“list”
open=“(“ separator=“,” close=“)”>
#{item}
</foreach></select>
5.别名
<typeAliases>
<typeAlias type=“org.sample.MyLanguageDriver” alias=“myLanguage”/></typeAliases><settings>
<setting name=“defaultScriptingLanguage” value=“myLanguage”/></settings>

1问题如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IsomeService{
void doFirst();
void dosecond();
}
public class SomeServiceImpl implements ISomeService{
public void doFirst(){
syso(执行事务代码);
syso(“执行doFirst()方法”);
}
public void dosecond(){
syso(执行事务代码);
syso(“执行dosecond()方法”);
}
}

在执行上面代码发现有两个重复代码,就算提取出来做一个方法,但是还是需要在每个方法里调用,并且事务和主业务在一起,分不清楚。
结果:出来了aop,把事务都写在xml的配置文件,之后就能直接处理主方法了。

1.返回的是一个对象

1
2
3
<select id=“selectPerson” parameterType=“int” resultType=”对象的名字”>
SELECT * FROM PERSON WHERE ID = #{id}
</select>

2.返回的list

1
2
3
4
List<Student> student=sqlsession.selectList(““selectPerson”,student);
<select id=“selectPerson” parameterType=“int” resultType=”对象的名字“>
SELECT * FROM PERSON WHERE ID = #{id}
</select>

3.返回的是map

1
2
3
4
Map<String,Student> student=sqlsession.selectMAP(““selectPerson”,查询出来的属性值);
<select id=“selectPerson” parameterType=“int” resultType=”对象的名字“>
SELECT * FROM PERSON WHERE ID = #{id}
</select>

4.模糊查询

1
2
3
4
5
<select id=“selectPerson” parameterType=“int” resultType=”对象的名字“>
写法1: SELECT * FROM PERSON WHERE name like ‘%’ #{xxx} ‘%’
写法2: SELECT * FROM PERSON WHERE name like concat(‘%’, #{xxx}, ‘%’)
写法3: SELECT * FROM PERSON WHERE name like ‘%${value}%’
</select>

5.别名不一样,解决方式
解决方式1:

1
2
3
<select id=“selectPerson” parameterType=“int” resultType=”对象的名字“>
SELECT tid id,tname name FROM PERSON WHERE ID = #{id}
</select>

解决方式2:直接定义一个resultmap

```java


oken,就是令牌,最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。
那么,Token有什么作用?又是什么原理呢?
Token一般用在两个地方:

  1. 防止表单重复提交、
  2. anti csrf攻击(跨站点请求伪造)。
    两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。

然后,如果应用于“anti csrf攻击”,则服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。

不过,如果应用于“防止表单重复提交”,服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。
上面的session应用相对安全,但也叫繁琐,同时当多页面多请求时,必须采用多Token同时生成的方法,这样占用更多资源,执行效率会降低。因此,也可用cookie存储验证信息的方法来代替session Token。比如,应对“重复提交”时,当第一次提交后便把已经提交的信息写到cookie中,当第二次提交时,由于cookie已经有提交记录,因此第二次提交会失败。

不过,cookie存储有个致命弱点,如果cookie被劫持(xss攻击很容易得到用户cookie),那么又一次gameover。黑客将直接实现csrf攻击。

所以,安全和高效相对的。具体问题具体对待吧。

此外,要避免“加token但不进行校验”的情况,在session中增加了token,但服务端没有对token进行验证,根本起不到防范的作用。

还需注意的是,对数据库有改动的增删改操作,需要加token验证,对于查询操作,一定不要加token,防止攻击者通过查询操作获取token进行csrf攻击。但并不是这样攻击者就无法获得token,只是增大攻击成本而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.security.MessageDigest;  
import java.security.NoSuchAlgorithmException;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;

import sun.misc.BASE64Encoder;

/**
* @description: 令牌处理,防止重复提交
* @author: shaojie
* @date: 2014-5-21 下午02:32:49
* @version: 1.0
*/
public class TokenProcessor {

/**
* 生成一个令牌
* @return String
*/
public String generateToken() {
String token=System.currentTimeMillis()+new Random().nextInt()+“”;
try {
MessageDigest md=MessageDigest.getInstance(“md5”);
byte[] md5=md.digest(token.getBytes());
//base64编码
BASE64Encoder encoder=new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}

/**
* 管理令牌
* @param request
* @param mType 管理类型 p:存放 d:删除
* @param void
*/
public void managerToken(HttpServletRequest request,String mType){
if(“p”.equals(mType)){
//产生随机数
String token=generateToken();
request.getSession().setAttribute(“token”, token);
}else if(“d”.equals(mType)){
request.getSession().removeAttribute(“token”);
}
}

/**
* 令牌是否验证通过
* @param request
* @return boolean
*/
public boolean isTokenValid(HttpServletRequest request) {
String client_token = request.getParameter(“token”);
if (client_token == null) {
return false;
}
String server_token = (String) request.getSession().getAttribute(“token”);
if (server_token == null) {
return false;
}
if (!client_token.equals(server_token)) {
return false;
}
return true;
}
}

然后在适当的位置存放令牌

1
token.managerToken(request, “p”);//存放令牌

然后提交订单时删除对应的令牌存放值

1
2
3
4
5
6
if(!token.isTokenValid(request)){  
logger.info(“重复提交,phoneNo:”+phoneNo);
request.setAttribute(Constant.RETURNMSG, “不能重复提交同一订单”);
return checkresult;
}
token.managerToken(request, “d”);//删除令牌