- 浏览: 33572 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
wangyudong:
Holer是一个免费开源的内网穿透工具,它可以将局域网服务器代 ...
外网端口映射利器ngrok -
sun20120718:
可以吗? 我试了一下 好像不行吧
关于spring quartz定时任务执行带参数的方法的问题
收藏列表
- 全部 [7]
- struts2 [1]
- hibernate [3]
- ibatis [1]
- spring aop [1]
- spring mvc [1]
- restful [1]
- security authorize [1]
标题 | 标签 | 来源 | |
security authorize 标签 | security authorize | ||
authorize用来判断当前用户的权限,然后根据指定的条件判断是否显示内部的内容。 jsp页面首先要引入security的标签库 代码: <%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %> 代码: //ifAllGranted,只有当前用户同时拥有ROLE_ADMIN和ROLE_USER两个权限时,才能显示标签内部内容。 <security:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER"> admin and user </security:authorize> //ifAnyGranted,如果当前用户拥有ROLE_ADMIN或ROLE_USER其中一个权限时,就能显示标签内部内容。 <security:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER"> admin or user </security:authorize> //ifNotGranted,如果当前用户没有ROLE_ADMIN时,才能显示标签内部内容 <security:authorize ifNotGranted="ROLE_ADMIN"> not admin </security:authorize> |
|||
S标签实际大全 | struts2 | ||
(1):<s:textfield> ---- 文本输入框? 使用:<s:textfield name=”实体Bean。属性”></s:textfield>? (2):<s:textarea> ----- 文本域输入框? 使用:<s:textarea name=”实体Bean。属性”></s:textarea>? (3):<s:password> ----- 密码输入框? 使用:<s:password name=”实体Bean。属性”></s:password>? 前三个基本都是一样的。如果是要显示值的话可以这样:value = “实体Bean。Get***()”。? (4):<s:radio list=””> ---- 单选按钮? 使用:<s:radio list=””>其中list 属性是必须要有的。? <1>:第一种方式:list = “#{‘male’:’男’,’female’:’女’}”? <2>:第二中方式:list = “#request/session.list”。 ---- 与action结合起来了。? 如果要默认选中的话后面加上value = “”.也可以通过javascript的方式来默认选中。? (5):<s:url/> --- url连接? <s:a/> --- 超链接? 这两个标签一般结合起来来使用。? 使用:<s:url id = “id” action = “userAction”/><s:a href=”%{id}”/>? (6):<s:form/> --- 获取相应form的值? 使用:? <1>:<s:form action = “userAction”>? <2>:<s:url id=”userId” action = “userAction”> -- 定义了一个userId的指向userActionde 路径? <s:form action=”%{userId}”>? (7):<s:submit/> ---- 提交标签? (8):<s:reset/> ---- 重置标签? (9):<s:hidden/> ---- 隐藏域标签? 使用:<s:hidden name=”实体Bean。属性” value=”%{#request/session.实体Bean。属性}”/>? (10):<s:combobox/> ---- 下拉框配合输入框一起使用? 使用:<s:combobox name=”age” list=””>? <1>:list = “{18,20,30}”? <2>:list = “#request/session.list”? (11):<s:checkbox/> ---- 多选框? 使用:<s:checkbox name=”自己随便起” value = “值” > 足球? (12):<s:checkboxlist/> ---- 复选框? 使用:<s:checkboxlist name=”自己随便起” label = “标签前面的名字” list=””>? <1>:list = “{‘hibernate’, ‘spring’, ‘strust2’}”? <2>:list = “#request/session.list”.? 如果想默认选中的话,那么加上value=””? (13):<s:if test=""></s:if>? <s:elseif test=""></s:elseif>? <s:else></s:else> ---- -这3个标签一起使用,表示条件判断? 使用:? <s:if test="%{false}">? <div>Will Not Be Executed</div>? </s:if>? <s:elseif test="%{true}">? <div>Will Be Executed</div>? </s:elseif>? <s:else>? <div>Will Not Be Executed</div>? </s:else>? (14):<s:div/> --- 表示一个块,类似与html中的div? (15):<s:generator/> ---- 一般和<s:iterator/>一起使用。? 使用:? <s:generator separator = “,” val=”%{aaa, bbb, ccc, ddd, eee}”>? <s:iterator>? <s:property/><br />? </s:iterator>? </s:generator>? <s:iterator value=”days” status=”d”>? <s:property name=”d.***”/>? </s:iterator>? Days表示:在action中存的值,status :表示起的别名。<s:property name=”d.***”>循环的一个个属性。? (16):<s:select/> ---- 下拉框的使用? 使用:? <s:select label=”请选择” list=”{‘book’, ‘pen’, ‘moon’}” value=”%{‘pen’}”>? Value : 表示默认值。? (17):<s:bean/> ----- Bean标签,当然需要一个JavaBean。它的属性值的操作是经由Bean标签中的参数属性来进行赋值。当然,它还有一个id属性可以进行赋值,这样就可以在上下文中使用这个Bean.? 使用:<s:bean name=”le.tks.Books” id=”book”>? <s:param name=”bookName”>jsf</s:param>? <s:property name=”%{bookname}”/>? </s:bean>? 页面输出的结果:jsf。? (18):<s:date/> ---- 方便在页面中进行格式化的输出。? 使用:? <s:date name=”currentDate” format=”dd/MM/yyyy”>? 页面中显示的结果:24/09/2008. 格式还有很多:MM/dd/yyyy、MM/dd/yyyy hh:mm:ss等。? (19):<s:include/> ---- 包含标签,是把这个页面中的所有的内容都包含进来。? 使用:<s:include name=”/**/**/**.jsp”/>? (20):<s:param/> --- param标签用于传递参数,如给<s:bean>标签传递参数,它有两个属性:? <1>:name(String):参数名。? <2>:value(Object):参数值。? 使用:? <a href="? <s:url action="book">? <s:param name="gId" value="20"></s:param>? <s:param name="operate" value="50"></s:param>? <s:param name="projectName" value="30"></s:param>? </s:url>">? insert</a>? (21):<s:set/> --- Set标签用户将某一值赋给某一变量,因此,任何对该项值的引用都可以通过该变量来得到该值.? 使用:? <s:set name="teacher" value="%{'数学高级教师'}"/>? 教师职称:<s:property value="#teacher"/>? 页面显示:教师职称:数学高级教师。? (22):<s:token/> ---- 防止重复提交? 使用:在页面加载时,<s:token/>会产生一个GUID(Globally Unique Identifier,? 全局唯一标识符)值的隐藏输入框如:? <input type="hidden" name="struts.token.name" value="struts.token"/>? <input type="hidden" name="struts.token" value="BXPNNDG6BB11ZXHPI4E106CZ5K7VNMHR"/>? 放在页面中随便的一个地方。<s:token/> |
|||
Hibernate集合条件的查询 | hibernate | ||
查数据: String hql2 = "from Customer c " + "left join fetch c.orders o " + "where o.orderName like '%iphone%'"; List<Customer> cs = session.createQuery(hql2).list(); 统计总记录条数: String hql = "select count(*) from Customer c " + "left outer join c.orders o " + "where o.orderName like '%iphone%'"; session.createQuery(hql).uniqueResult(); 请注意,统计记录条数时使用的是left outer join 。在分页中,需进行replace。 一段代码示例: public Page getAllIncomeStorage(final int begin,final int quantity,final Map map) throws Exception { return (Page)dao.getHibernateTemplate().execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql = "from StorageIncomeStorage s left join fetch s.cargo c where 1 = 1"; if(map.get("begin") != null){ hql +=" and s.incomeTime > :begin"; } if(map.get("end") != null){ hql +=" and s.incomeTime < :end"; } if(map.get("pact") != null){ hql += " and c.pact.id = :pact"; } if(map.get("company") != null){ hql += " and c.company.id = :company"; } hql += " and c.color like :color"; Page page = new Page(begin,quantity); page.setData(session.createQuery(hql) .setProperties(map) .setFirstResult((begin - 1) * quantity) .setMaxResults(quantity) .list() ); String countHql = "select count(*)" + hql.replace("left join fetch", "left outer join "); page.setPageSize(((Number)session.createQuery(countHql) .setProperties(map) .uniqueResult()).intValue()); page.setTotalRows(page.getData().size()); return page; } }); } |
|||
Spring Aop实现机制 | spring aop | ||
spring对持久层的事务管理采用的aop实现,在spring底层,aop是使用的代理模式实现的,代理模式在java中一般有jdk 动态代理以及cglib实现的更为高效的代理,jdk动态代理采用的是标准代理模式(大家可以看看标准代理模式的uml图),被代理对象必须要实现接口,而cglib就没有这一强制性要求了。 下面,我要实现一个环绕通知,分别用jdk动态代理,cglib,以及在spring中的配置实现。 我有两个接口,IMyService1,IMyService2,实现类MyServiceImpl实现了这两个接口,另外,还写了一个没有实现任何接口的service ,NoInterfaceService。 一、jdk动态代理 MyInvocationHandler.java //调用处理程序,相当于aop中的环绕通知 public class MyInvocationHandler implements java.lang.reflect.InvocationHandler { //被代理的对象 private Object target; //构造方法传入被代理的对象 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //proxy就是$proxy0,里面包含一个target属性,为代理的目标对象 System.out.println(method.getName() + "方法调用前!"); Object result = method.invoke(target, args); System.out.println(method.getName() + "方法调用后!"); return result; } } 测试代码: //目标对象 MyServiceImpl ms = new MyServiceImpl(); //创建代理对象,interface是代理要 IMyService1 ms1 = (IMyService1)Proxy.newProxyInstance(ms.getClass().getClassLoader(), ms.getClass().getInterfaces(), new MyInvocationHandler(ms)); ms1.biz1(); IMyService2 ms2 = (IMyService2)ms1; ms2.biz2(); 请大家注意,需要动态代理的接口,由上面ms.getClass().getInterfaces()的代码传入,并且,我们可以发现,代理产生的对象,可以强转为其中任意一个接口,但如果强转为MyServiceImpl会报错:$proxy0不能转换为MyServiceImpl。 二、cglib实现 MyAdvice.java //这个是通知类 class MyAdvice implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println(method.getName() + "执行之前.............."); Object result = methodProxy.invokeSuper(object, args); System.out.println(method.getName() + "执行之后.............."); return result; } } 产生代理的测试类: Enhancer eh = new Enhancer(); eh.setSuperclass(NoInterfaceService.class); //如果不需要通知代码 //eh.setCallback(NoOp.INSTANCE); eh.setCallback(new MyAdvice()); NoInterfaceService ns = (NoInterfaceService)eh.create(); ns.hello(); 上面的代理,可以对无接口对象实现代理,有接口的当然也是不存在问题的。 三、spring代理配置实现 LoggerAdvice.java public class LoggerAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println(mi.getMethod().getName() + "开始执行!!!"); Object result = mi.proceed(); System.out.println(mi.getMethod().getName() + "执行完毕!!!"); return result; } } 配置文件: <!-- 代理对象 --> <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean" > <property name="target" ref="myService"></property> <property name="interceptorNames"> <list> <value>loggerAdvice</value> </list> </property> </bean> 测试代码: BeanFactory bf = new ClassPathXmlApplicationContext("beans.xml"); IMyService1 ms1 = (IMyService1)bf.getBean("myServiceProxy"); ms1.biz1(); IMyService2 ms2 = (IMyService2)bf.getBean("myServiceProxy"); ms2.biz2(); 如果被代理对象实现了接口,默认情况下spring会使用jdk动态代理,因此,如果代把上面取到的对象强转为MyServiceImpl会报错:$proxy0不能转换为MyServiceImpl,但转换为其它所实现的两个接口,都不会存在问题。 我们也可以通过proxyInterfaces指定,代理哪个接口(默认情况下,会自动代理该类所实现的所有接口)。 <property name="proxyInterfaces"> <list> <value>com.my.service.IMyService1</value> </list> </property> 如果被代理的类没有实现接口,spring会自动使用cblib作为底层代理实现,如果你的lib下没有该jar包,系统会报错。 当然,在被代理的类有接口的情况下,你也可以通过配置,强制使用cglib代理,配置如下: <property name="proxyTargetClass" value="true"></property> 以下代码中可以看出,代理对象可直接强转为实现类: NoInterfaceService is = (NoInterfaceService)bf.getBean("myServiceProxy"); is.hello(); |
|||
Hibernate多对多查询 | hibernate | ||
在hibernate中,一般情况下要减少多对多关系的存在,主要原因是性能。hibernater中的多对多是采用的内存分页,如果你的数据量比较大,就很杯具了。如果遇到多对多,我们可以为中间表建立新的对象,从而转换为多对一关系。 下面大家看一个用户和用户组多对多的hql分页写法。 //下面查到的就是当某一页的数据,hibernate使用的是内在分页,使用distinct由hibernate去除重复 String hql = " select distinct(u) from GameUser u left join fetch " + "u.groups g where g.name like '%组%'"; List<GameUser> gus = session.createQuery(hql) .setFirstResult(0).setMaxResults(1).list(); 在JPA规范中,你可以看到: “The effect of applying setMaxResults or setFirstResult to a query involving fetch joins over collections is undefined” 如果细心观察过日志,你也会发现hibernate有如下输出: WARNING: firstResult/maxResults specified with collection fetch; applying in memory! 这就说明,使用了left join的语句无法再使用数据库分页,和多对多关系,没有必然联系。 //这是统计总记录条数的hql,需要使用in String hql4cont = "select count(*) from GameUser gu where gu.id in (select u.id from GameUser u left join " + "u.groups g where g.name like '%组%')"; Number count = (Number)session.createQuery(hql4cont).uniqueResult(); 那么,如何将上面的多对多关系改造成一对多关系呢,当然是要对中间表建立对象了,如下: 用户对象: GameUser.java @Entity @Table(name="t_gameuser") public class GameUser { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="pk_id") private int id; @Column(name="f_name") private String name; @OneToMany(mappedBy="user",cascade=CascadeType.ALL, fetch=FetchType.LAZY) @JoinColumn(name="fk_user_id") @BatchSize(size=2) private Set<UserInGroup> userInGroupSet = new HashSet<UserInGroup>(); 用户组对象: Group.java @Entity @Table(name="t_group") public class Group { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="pk_id") private int id; @Column(name="f_name") private String name; @OneToMany(mappedBy="group") @JoinColumn(name="fk_group_id") private Set<UserInGroup> userInGroupSet = new HashSet<UserInGroup>(); 中间对象(中间对象中引用对象加载方式,请大家设置为EAGER以提升系统查询效率,cascade须设置为save_update): UserInGroup.java @Entity @Table(name="t_user_group") public class UserInGroup { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="pk_id") private int id; @ManyToOne(fetch=FetchType.EAGER) @Cascade(value={org.hibernate.annotations.CascadeType.SAVE_UPDATE}) @JoinColumn(name="fk_user_id") private GameUser user; @ManyToOne(fetch=FetchType.EAGER) @Cascade(value={org.hibernate.annotations.CascadeType.SAVE_UPDATE}) @JoinColumn(name="fk_group_id") private Group group; 大家注意一下,中间对象的主键id实际并无意义,添加上只是为了映射方便,你也可以将两外键建成联合主键(但似乎jap注解方式不支持这种配置, xml中倒是可以做到)。 最后看分页查询怎么写: String hql = "select distinct(uig.user) from UserInGroup uig " + "where uig.group.name like '%组%' and uig.user.name like '%%'"; List<GameUser> gus = session.createQuery(hql) .setFirstResult(0).setMaxResults(100).list(); for (GameUser gu : gus) { System.out.println(gu.getName()); System.out.println(gu.getUserInGroupSet().size()); } 这个时候,将是纯正的数据库级分页,很好地满足了我们的要求。 String hql4cont = "select distinct(uig.user.id) from UserInGroup uig " + "where uig.group.name like '%组%' and uig.user.name like '%%'"; Number count = (Number)session.createQuery(hql4cont).list().size(); 统计总记录数的hql,就像上面这个写法,就ok了,因为只取的id,没有创建完整的对象,性能不存在大的问题。 但是大家要注意,这个时候我们没有用left join之类的加载方式,意味着查出来的对象,其中的集合属性没有初始化,如果在后续业务中需要访问集合属性,hibernate会再次去查询加载,为了性能考虑,我们一般会加上@BatchSize注解为提高集合加载效率,如果不写@BatchSize,默认是一条一条加载的,写上之后,会有类似下面的sql出现,?的最大个数,就是BatchSize设定的大小: select useringrou0_.fk_user_id as fk3_1_, useringrou0_.pk_id as pk1_1_, useringrou0_.pk_id as pk1_9_0_, useringrou0_.fk_group_id as fk2_9_0_, useringrou0_.fk_user_id as fk3_9_0_ from t_user_group useringrou0_ where useringrou0_.fk_user_id in ( ?, ? ) 在一般的项目中,我们把@BatchSize设置界面每页显示记录数一致即可,多对多转换为一对多,关系越简单越好。 |
|||
项目中ibatis与hibernate混用示例详解 | ibatis, hibernate | ||
对于一般的项目hibernate足以胜任,但在选择一个框架时都要考虑适用场景(比如非功能性需求),hibernate也不例外,比如:复杂多条件组合查询对于hibernate来说并不方便(我们需要手工拼接sql)。基于这样的原因,很多项目中会引入ibatis来做复杂查询操作以做为补充,这主要是针对编码复杂度的考虑,性能也是其次的一个因素(其实hibernate也不存在问题的,你还记得到hibernater中有SqlQuery么,呵呵)。 下面我给出一个简单的ibatis与hibernate混用示例,并在最后说明在实际使用过程中应该注意的问题。先来看spring的核心配置文件: spring-core.xml <!-- 读取配置参数 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"> <value>classpath:database.properties</value> </property> </bean> <!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close" > <property name="driverClassName"> <value>${driverClassName}</value> </property> <property name="url"> <value>${url}</value> </property> <property name="username"> <value>${username}</value> </property> <property name="password"> <value>${pwd}</value> </property> <property name="defaultAutoCommit"> <value>false</value> </property> <property name="initialSize" value="1" /> <property name="filters" value="stat,log4j" /> <property name="name" value="myDatasource1"></property> <!-- 最大活动连接数,也就是连接池中的最大缓存连接数 --> <property name="maxActive" value="20" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="10000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="10000" /> <property name="minIdle" value="1" /> </bean> <!-- hibernate参数配置文件,包括缓存的信息 --> <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location"> <value>classpath:hibernate-config.properties</value> </property> </bean> <!-- 配置sessionFactory--> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /> </property> <property name="hibernateProperties" ref="hibernateProperties"></property> <property name="packagesToScan"> <list> <value>com.my.monitor.model</value> </list> </property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 对注解事务的支持 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 注解驱动 --> <context:component-scan base-package="com.my.monitor" ></context:component-scan> <!-- hibernate通用dao --> <bean id="hibernateBaseDao" class="com.my.ms.framework.persistence.hibernate.BaseDaoHibernateImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- ibatis通用dao,一般用于查询 --> <bean id="ibatisBaseDao" class="com.my.ms.framework.persistence.ibatis.BaseDao"> <property name="sqlMapClient"> <bean class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation"> <value>classpath:config/ibatis/sqlMapConfig.xml</value> </property> </bean> </property> </bean> 这里封装了两个baseDao,分别针对的是hibernate与ibatis书写,源代码如下: BaseDaoHibernateImpl.java package com.my.ms.framework.persistence.hibernate; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.util.Assert; import com.my.ms.framework.persistence.model.Page; public class BaseDaoHibernateImpl extends HibernateDaoSupport implements IBaseDao { @Override public void addEntity(Object entity) { getHibernateTemplate().save(entity); } @Override public void updateEntity(Object entity) { getHibernateTemplate().update(entity); } @Override public void deleteEntity(Object entity) { getHibernateTemplate().delete(entity); } @Override public void deleteEntityById(Class clazz, Serializable id) { getHibernateTemplate().delete(this.queryEntityById(clazz, id)); } @Override public <T> T queryEntityById(Class<T> clazz, Serializable id) { return getHibernateTemplate().get(clazz, id); } @Override public List queryEntitys(String queryString, Object[] values) { return getHibernateTemplate().find(queryString, values); } @Override public Page queryEntityByPage(int pageNo, int pageSize, String queryString, Object[] parameters) { Assert.isTrue(pageNo > 0, "起始页不能小于0!"); // 去除select 子句,未考虑union的情况 int beginPos = queryString.toLowerCase().indexOf("from"); Assert.isTrue(beginPos != -1, queryString + "无效,必须包含from关键字"); String hql4Count = " select count(*) " + queryString.substring(beginPos); return queryEntityByPage(pageNo, pageSize, queryString, hql4Count, parameters); } @Override public Page queryEntityByPage(final int pageNo, final int pageSize, final String queryString4Data, final String queryString4Count, final Object[] parameters) { Assert.hasText(queryString4Count, "用于计数的hql不能为空!"); Assert.hasText(queryString4Data, "用于查询的hql不能为空!"); Assert.isTrue(pageNo > 0, "起始页不能小于0!"); return (Page) getHibernateTemplate().execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException { // 根据指定的参数执行hibernate hql查询 List countlist = getHibernateTemplate().find(queryString4Count, parameters); long totalCount = ((Long) countlist.get(0)).longValue(); // 如果记录总数小于1则返回空的Page if (totalCount < 1) return new Page(pageNo, pageSize, new ArrayList(), 0); int startIndex = Page.getStartOfPage(pageNo, pageSize); Query query = getSession().createQuery(queryString4Data); for (int i = 0; i < parameters.length; i++) { query.setParameter(i, parameters[i]); } List list = query.setFirstResult(startIndex) .setMaxResults(pageSize).list(); return new Page(pageNo, pageSize, list, (int) totalCount); } }); } } 针对ibatis的baseDao封装,主要是添加了一个分页方法,代码如下: BaseDao.java package com.my.ms.framework.persistence.ibatis; import java.util.Map; import org.springframework.orm.ibatis.SqlMapClientTemplate; import com.my.ms.framework.persistence.model.Page; public class BaseDao extends SqlMapClientTemplate { /** * 分页查询(sql语句块中的分页参数,请使用start与end) * @param pageNo * @param pageSize * @param sqlId4Data * @param sqlId4count * @param queryParam * @return */ public Page queryEntityByPage(int pageNo, int pageSize, String sqlId4Data, String sqlId4Count, Map queryParam) { queryParam.put("start", (pageNo - 1) * pageSize); queryParam.put("end", pageSize); Page page = new Page(pageNo, pageSize); page.setData(queryForList(sqlId4Data, queryParam)); page.setRowcounts(((Number)queryForObject(sqlId4Count, queryParam)).intValue()); return page; } } 上面两个实现的分页部分,用到了Page对象,请看分页对象的代码: package com.my.ms.framework.persistence.model; import java.util.List; /** * 分页对象 * @author ljh * */ public class Page<T> { /** * 数据对象 */ private List<T> data; private int rowcounts; /** * 页码 */ private int pageNo; /** * 最大显示页数 */ private int pageSize; public Page(){ } public Page(int pageNo, int pageSize) { this.pageNo = pageNo; this.pageSize = pageSize; } public Page(int pageNo, int pageSize, List<T> data, int rowcounts) { this.pageNo = pageNo; this.pageSize = pageSize; this.data = data; this.rowcounts = rowcounts; } //计算该页对应的数据库下标 public static int getStartOfPage(int pageNo, int pageSize) { if (0 > pageNo) throw new IllegalArgumentException("页面索引不能小于0!"); return (pageNo - 1) * pageSize; } //共有多少页 public int getPages() { if (rowcounts % pageSize == 0) { return rowcounts/pageSize; } else { return rowcounts/pageSize + 1; } } public int getFirstNo() { return 1; } public int getLastNo() { return getPages(); } public int getPreNo() { if (pageNo - 1 > 0 ) { return pageNo - 1; } else { return 1; } } public int getNextNo() { if (pageNo + 1 <= getPages()) { return pageNo + 1; } return getPages(); } public List<T> getData() { return data; } public void setData(List<T> data) { this.data = data; } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { this.pageNo = pageNo; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public int getRowcounts() { return rowcounts; } public void setRowcounts(int rowcounts) { this.rowcounts = rowcounts; } } 具体使用时就比较方便了,下面是一个调用示例: UserServiceImpl.java @Service @Transactional(readOnly=false) public class UserServiceImpl implements IUserService{ @Resource private IBaseDao hibernateDao; @Resource private BaseDao ibatisDao; @Override public User someBizMethod(int userId) { ...... User u1 = (User)hibernateDao.queryEntityById(User.class, userId); u1.setUserName(u2.getUserName() + "hibernate"); hibernateDao.updateEntity(u1); User u2 = (User)ibatisDao.queryForObject("znjk.getUserById", userId); u2.setUserName(u2.getUserName() + "ibatis"); ibatisDao.update("znjk.updateUser", u2); ...... 好了,在上面的示例中还存在一些必须要弄明白的问题。 一、事务管理问题。大家可以看到,我在spring文件中只配置了一个事务管理器,并且是hibernate的事务管理器,那么,有人就有会疑问:hibernater的事务可以应用在ibatis的dao上么?答案是肯定的。至于原因,你想想spring事务管理是通过AOP来实现的,并且最终映射到底层的话,事务是通过在jdbc的connection上完成的,而在我上面的这个业务方法中,两个dao拿到的是同一个连接,因此,方法中的所有操作都在一个事务环境中了,这个事务是通过hibernate事务管理器启动的。 二、缓存同步问题。两个框架都有自己不同的缓存配置及实现,而且互不相关。因为这个原因,我上面的代码没法对保证缓存的一致性,所以,我的建议是:ibatis仅仅只做复杂查询,hibernate什么都可以做。此外,还有一个方案是,配置ibatis的数据源事务管理器,在不同的方法中,通过@Transactional注解来指定这个业务方法使用的事务管理器(比如:@Transactional(readOnly=false,value=“dataSourceTransactionManager”)),也就是说,在每一个方法中不存在两个dao混用的情况,而在不同的方法中,用不同的事务管理器操作事务的提交。 ibatis的优势在于查询,特别是类似下面的组合查询,十分的方便。复杂查询更是如此。 <sql id="where4pet"> <dynamic prepend="where"> <isNotNull property="nickName" prepend="and"> f_nick_name like '%$nickName$%' </isNotNull> <isNotNull property="password" prepend="and"> f_password like '%$password$%' </isNotNull> <isNotNull property="birthday" prepend="and"> f_birthday = #birthday# </isNotNull> <isNotNull property="gender" prepend="and"> <isEqual compareProperty="gender" compareValue="true"> f_gender = 'T' </isEqual> <isEqual compareProperty="gender" compareValue="false"> f_gender = 'F' </isEqual> </isNotNull> <isNotNull property="age" prepend="and"> f_age = #age# </isNotNull> </dynamic> </sql> <select id="getPetByPage4data" resultMap="resultMapPet" > select * from t_pet <include refid="where4pet"/> order by $field$ limit #start#, #end# </select> <select id="getPetByPage4count" resultClass="int"> select count(*) from t_pet <include refid="where4pet"/> </select> 这样的话,我们就可以充分利用不同框架的优势,达到取长补短的目的。也许你会说,这种方式就是把增、删、改交由hibernate来做,把查询交由ibatis来做。本质上看确实如此,但实际项目中要灵活去做,比如hibernate除了增删改外也可以做查询,特别是简单的查询,这样也可以更好的利用到hibernate的缓存。 我在四种持久层设计方案比较中,对查询的分离有所介绍,有兴趣的可以去看看其中的一个方案,另外,CQRS架构、mysql读写分离也有这方面的含义。 |
|||
Spring Mvc Restful服务器端数据验证实现 | spring mvc, restful | Spring Mvc Restful服务器端数据验证实现 | |
服务器端的数据合法性验证非常有必要而且是必须的,特别是restful api中场景中存在多种类型的客户端,你不能保证每种客户端都已经做好了本地化的数据合法性验证。 对于业务逻辑来说,我们一般会对新增业务操作以及修改业务操提交上来的数据进行验证,而一般的mvc框架都有对验证功能的封装集成,spring mvc也不利外,但这些集成的验证都和特定的视图标签有密切的联系,比如jsp标签库,而在我所说的restful场景中,服务器上往往不做页面的生成工作,那自然是用到到这些标签了。怎么办呢?只有自己做一些封装和改造了,简单的说,就是我们自己去解析BindingResult中的信息。 请看我下面封装的通用数据返回类代码,其中sendValidationFaildResponse方法会将验证出来的错误信息返回给客户端: RespMessage.java import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import com.my.ms.framework.commons.JsonMapper; /** * 向客户端返回的统一消息 * @author ljh * */ public class RespMessage { public static final int API_PARAM_ERROR = 10; public static final int SUCCESS = 1; public static final int FAIL = 0; public static final int DATA_VALIDATION_ERROR = -1; //操作状态码 0:操作失败,1:成功, 其它数据,由客户自定义 //-1:数据验证失败 , 其它取值含义由客户自定义 //10:api调用出错参数错误(特别是在无状态webservice之类的调用情况下) private int code = SUCCESS; //要返回的消息 private String msg; //返回的数据 private Object data; //消息版本,保留待用 private String version; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public boolean sendValidationFaildResponse( BindingResult result, JsonMapper mapper, HttpServletResponse resp) { if (result.getFieldErrors().size() > 0) { this.code = DATA_VALIDATION_ERROR; //这里只取字段类型的错误 List<FieldError> errors = result.getFieldErrors(); Map error_map = new HashMap(); for (FieldError e : errors) { error_map.put(e.getField().toString(), e.getDefaultMessage()); } this.data = error_map; this.msg = "你提交的数据没有通过验证!"; try { mapper.writeValue(resp.getOutputStream(), this); } catch (Exception ex) { ex.printStackTrace(); } return true; } return false; } } 接下来再来看看添加数据的action代码,这段代码中首先进行了数据合法性的验证: UserAction.java @RequestMapping(value="/user", method=RequestMethod.POST) public void addUser( @Valid User user, BindingResult result ) { resp.setCharacterEncoding("utf-8"); RespMessage respMsg = new RespMessage(); //进行数据合法性的验证 if (respMsg.sendValidationFaildResponse(result, jm, resp)) { return ; } //.... other porcess 其它代码略去 } 实体对象中,添加字段验证注解,如下(这里暂时没有使用国际化): User.java @Entity @Table(name="t_user") @JsonFilter("video_user") public class User implements Serializable { @Id @Column(name="pk_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int id; @Column(name="f_name") private String name; @Column(name="f_code") @NotEmpty(message="编码不能为空!") private String code; @Column(name="f_password") @NotEmpty(message="登录密码不能为空!") private String password; //................其它代码略去 最后,将验证相关的bean对象加入到apirng的配置文件中。 spring-action.xml <!-- 读取国际化资源配置文件 --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="res/appResources"></property> </bean> <!-- 添加验证对国际化的支持 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="validationMessageSource" ref="messageSource"></property> </bean> <!-- 启用spring mvc注解驱动 --> <mvc:annotation-driven validator="validator" /> 国际化的部分仅仅是为了以后扩展方便,同时需要你在src下建立res目录,并写一个空的appResources.properties文件。 针对上面的action访问,如果我们不输入必须要填写的字段,我们会得到如下的输出结果: { "code":-1, "msg":"你提交的数据没有通过验证!", "data": { "code":"编码不能为空!", "password":"登录密码不能为空!" } } 这个json信息,在jquery中可以通过下面的代码解析出错误详细信息,并进行显示: success:function(data){ if (data.code == -1) { $.each(data.data,function(key,data){ alert(key + ": " + data); }); return; } //........其它代码略去 |