Mysql5的存储过程的权限Bug
java.sql.SQLException: Driver requires declaration of procedure to either contain a ‘\nbegin’ or ‘\n’ to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.
at com.mysql.jdbc.DatabaseMetaData.getCallStmtParameterTypes(DatabaseMetaData.java:6953)
at com.mysql.jdbc.DatabaseMetaData.getProcedureColumns(DatabaseMetaData.java:2721)
at com.mysql.jdbc.CallableStatement.determineParameterTypes(CallableStatement.java:1048)
at com.mysql.jdbc.CallableStatement.<init>(CallableStatement.java:83)
at com.mysql.jdbc.Connection.prepareCall(Connection.java:1255)
at com.mysql.jdbc.Connection.prepareCall(Connection.java:1232)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.mchange.v2.c3p0.stmt.GooGooStatementCache$2.run(GooGooStatementCache.java:333)
at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:354)
仔细思考,换用了root登陆就可以了。感觉明显是权限privileges的问题。
所以其实应该是这个提示or SELECT privilege on mysql.proc to parse column types。
可是google不到这个问题所在,因为没有mysql.proc这个权限,但是有人说这是一个bug。
后来我发现,其实它是指mysql这个库的proc表的SELECT权限,添加上就工作正常了……
其实原因是这样的,原先测试时使用的xinfangweb的用户有两个,一个是localhost,另一个是%,前者的权限是全的,后者被限制了,哈哈。
HQL的中文问题小议
hql的String中包括了中文以后就会出错。
经过多次查询,转码(多字节、单字节)还是不能解决问题,说明某些环节压根就对多字节支持不正常。
后来经过多次试验,发现是hibernate3的hql语法解释器org.hibernate.hql.ast.ASTQueryTranslatorFactory出的问题,看到有人推荐用org.hibernate.hql.classic.ClassicQueryTranslatorFactory代替。
那当然是不可以的,否则相当于退回Hibernate2。
解决方法是不要在hql中写中文。
而是去使用?占位符和setParameters。
比如在Spring HibernateTemplate里面可以:
String hql = "select count(*) from Xinfangnews xfnews where xfnews.shifoufabu = 0 and (xfnews.title like ? or xfnews.content like ?)";
Object[] parameters = { "%" + keyWord + "%", "%" + keyWord + "%" };
getHibernateTemplate().find(hql, parameters);
如此可以运行了。
不过我走了点弯路:
本来写成"like ‘%?%’",然后往里面插keyWord,告诉我找不到?占位符。后来才想到”中的?已经不是占位符了。
然后就解决了:
所以就是说不能在HQL进行语法解析之前插入中文,而需要在解析之后插入。
还要注意,我试验了一下,在Mysql5中已经指定了使用UTF-8语言的时候,不必须在Hibernate连接的地方指明使用jdbc的encoding:
如很多人说需要这样配置:<prop key="hibernate.connection.url">jdbc:mysql://10.206.21.171:3306/xinfangweb?useUnicode=true&characterEncoding=GBK&autoReconnect=true</prop>
但实际上是用这样的配置:<prop key="hibernate.connection.url">jdbc:mysql://10.206.21.171:3306/xinfangweb</prop>
也是可以工作的,当然还是写上比较稳妥。
Generic Hibernate DAO试验
我们项目升级Weblogic 9.1,所以可以用JDK5的东西了。
先试验一下GenericHibernateDAO,作为一个DAO的基类,实现了一些简单的方法,方便子DAO封装。
使用方法类似:
public class HibernateTownDAO extends GenericHibernateDAO<Town>
implements ITownDAO{
{
public HibernateTownDAO() { super(Town.class); } }
}
这个东西基本上是从EclipseWork里面来的……
http://www.matrix.org.cn/thread.shtml?topicId=34892&forumId=26
http://www.javaworld.com.tw/roller/page/ingramchen?entry=2005_10_20_G…
还参考了limo的Blog和limo与徐x赠送的部分代码,thx
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import java.util.Iterator;
* @author TIN
* @comment
* @param <E>
*
* 范型的抽象HibernateDAO,继承于Spring提供的HibernateDaoSupport,该DAO封装了大部分的Domain对象访问方法。
* 使用范型的目的在于可以显示的规定传入/返回的类型。直接的目的在于减少使用DAO时的强行类型转换。
*
* TODO:getTopsByProperty(int, String)方法
* TODO:execute(HibernateCallback callback)方法?
* TODO:execute(HQL hql)方法?
* TODO:execute(SQL sql)方法?
* TODO:findByCriteria(Criterion… criterion)方法?
* TODO:getAOfflineListCopy(List list)方法?获取一个离线list,可以在部分地方获取session无关的List,避免使用OpenSessionInView
* TODO:String getPersistentObjectName()方法?
* TODO:使用变长参数修改一些传入Properties和Value的地方,扩展一些方法
* TODO:研究一下DetachedCreteria
*
*/
this.clazz = clazz;
}
* @see com.goldnet.dao.IDAO.IGenericDAO#saveOrUpdate(java.lang.Object,
* java.io.Serializable)
* @deprecated
*
* @param newObject 持久化的类
* @param id 持久化类的标识id
*
* 存储一个Entity,由于它的实现方式与HibernateDAO的saveOrUpdate雷同,都检测了id是否存在,但却多一个参数
* 所以暂时部推荐,但是该实现对saveOrUpdate机理表现比较清晰,所以没有删除
*/
public void saveOrUpdate(E newObject, Serializable id) {
if ((id != null) && !getHibernateTemplate().contains(id)) {
E dbObject = this.loadById(id);
BeanUtils.copyProperties(dbObject, newObject);
} catch (Exception e) {
logger.error(e, e);
}
}
logger.debug("[saveOrUpdate with id Parameter] Entity<"
+ this.clazz.getName() + ">.");
logger
.debug("this method is deprecated! Please use saveOrUpdate(Object) instead.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#saveOrUpdate(java.lang.Object)
*
* @param newObject 持久化的类
*
* 持久化一个Entity,一般用在不知持久化的是VO或PO时
*/
public void saveOrUpdate(E newObject) {
if (logger.isDebugEnabled()) {
logger
.debug("[saveOrUpdate] Entity<" + this.clazz.getName()
+ ">.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#save(java.lang.Object)
*
* @param object 持久化的类
*
* 持久化一个Entity,用在持久化PO时
*/
public void save(E object) {
if (logger.isDebugEnabled()) {
logger.debug("[save] Entity<" + this.clazz.getName() + ">.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#update(java.lang.Object)
*
* @param object 持久化的类
*
* 持久化一个Entity,符合jsr-220规范,用在持久化一个VO的Entity(但是它已经在库中存在)时,持久化它并返回它的PO
*/
@SuppressWarnings("unchecked")
public E merge(E object) {
if (logger.isDebugEnabled()) {
logger.debug("[merge] Entity<" + this.clazz.getName() + ">.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#isEntityWithPropertyOfValueExist(java.lang.String,
* java.lang.Object)
*
* @param propertyName Entity属性的名称
* @param value Entity属性的值
* @return
*
* 具有propertyName的Entity是否存在?如果true则存在,如果false则不存在。
*/
public boolean isEntityWithPropertyOfValueExist(String propertyName,
Object value) {
if (logger.isDebugEnabled()) {
logger.debug("[isEntityWithPropertyOfValueExist] Entity<"
+ this.clazz.getName() + ">, propName=" + propertyName
+ ", value=" + value + ".");
}
"from " + this.clazz.getName() + " table where table."
+ propertyName + "=?", value).size() > 0) {
return true;
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#isEntityIdExist(java.io.Serializable)
*
* @param id Entity的标识id
* @return
*
* 具有该id的Entity是否存在?如果true则存在,如果false则不存在。用来检测一个VO是否已经有对应的PO存在
*/
public boolean isEntityIdExist(Serializable id) {
if (logger.isDebugEnabled()) {
logger.debug("[isEntityIdExist] Entity<" + this.clazz.getName()
+ "> with id=" + id + ".");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#delete(java.lang.Object)
*
* @param object Entity对象
*
* 删除一个Entity
*/
public void delete(E object) {
if (logger.isDebugEnabled()) {
logger.debug("[delete] Entity<" + this.clazz.getName() + ">.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#deleteById(java.io.Serializable)
*
* @param id Entity的标识id
*
* 根据Entity的标识id删除一个Entity
*/
public void deleteById(Serializable id) {
if (logger.isDebugEnabled()) {
logger.debug("[deleteById] Entity<" + this.clazz.getName()
+ "> with id=" + id + ".");
}
getHibernateTemplate().get(this.clazz, id));
}
* @see com.goldnet.dao.IDAO.IGenericDAO#deleteByProperty(java.lang.String,
* java.lang.Object)
*
* @param propertyName Entity属性的名称
* @param value Entity属性的值
*
* 删除一组具有相同propertyName属性值(value)的Entity,用来批量删除
*/
@SuppressWarnings("unchecked")
public void deleteByProperty(String propertyName, Object value) {
Iterator<E> result = listByProperty(propertyName, value).iterator();
delete(result.next());
}
logger.debug("[deleteByProperty] Entity<" + this.clazz.getName()
+ ">, propName=" + propertyName + ", value=" + value + ".");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#loadById(java.io.Serializable)
*
* @param id Entity的标识id
* @return
*
* 根据Entity的标识id获取一个Entity的PO
*/
@SuppressWarnings("unchecked")
public E loadById(Serializable id) {
if (logger.isDebugEnabled()) {
logger.debug("[loadById] Entity<" + this.clazz.getName() + ">, id="
+ id + ".");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#loadByProperty(java.lang.String,
* java.lang.Object)
*
* @param propertyName Entity属性的名称
* @param value Entity属性的值
* @return
*
* TODO:判断结果是否是unique?
* 获取一个具有相同propertyName属性值(value)的Entity,注意改属性必须是unique的,否则改方法将没有意义
*/
@SuppressWarnings("unchecked")
public E loadByProperty(String propertyName, Object value) {
Collection result = getHibernateTemplate().find(
"from " + this.clazz.getName() + " table where table."
+ propertyName + "=?", value);
Iterator<E> it = result.iterator();
logger.debug("[loadByProperty] Entity<" + this.clazz.getName()
+ ">, propName=" + propertyName + ", value=" + value + ".");
}
return it.next();
} else {
return null;
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#loadEntitySum()
*
* @return
*
* 获取一种Entity的总数
*/
public int loadEntitySum() {
if (logger.isDebugEnabled()) {
logger.debug("[loadEntitySum] Entity<" + this.clazz.getName()
+ ">.");
}
"select count(*) from " + this.clazz.getName()).iterator()
.next()).intValue();
}
* @see com.goldnet.dao.IDAO.IGenericDAO#loadEntitySumByProperty(java.lang.String,
* java.lang.Object)
*
* @param propertyName Entity属性的名称
* @param value Entity属性的值
* @return
*
* 获取一组具有相同propertyName属性值(value)的Entity的个数
*/
public int loadEntitySumByProperty(String propertyName, Object value) {
if (logger.isDebugEnabled()) {
logger.debug("[loadEntitySum] Entity<" + this.clazz.getName()
+ ">, propName=" + propertyName + ", value=" + value + ".");
}
"select count(*) from " + this.clazz.getName()
+ " table where table." + propertyName + "=?", value)
.iterator().next()).intValue();
}
* @see com.goldnet.dao.IDAO.IGenericDAO#loadLastEntity()
*
* @param orderByPropertyName 用来排序的属性的名称
* @return
*
* TODO:和获取Tops是否重复?
* 返回最后一个Entity,用来获取如最新加入的Entity这样的方法
* 注意改方法实际上返回的是降序排列的list中的第一个!
*/
@SuppressWarnings("unchecked")
public E loadLastEntity(final String orderByPropertyName) {
logger.debug("[listLastEntity] Entity<" + this.clazz.getName()
+ "> and orderByPropertyName=" + orderByPropertyName
+ ", Note:it’s DESC.");
}
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazz);
criteria.addOrder(Order.desc(orderByPropertyName));
releaseSession(session);
return criteria.list().iterator().next();
} catch (Exception e) {
logger
.warn("[listLastEntity] faild! the entity list is empty!");
}
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listAll()
*
* @return
*
* 获取所有的Entity的list
*/
@SuppressWarnings("unchecked")
public Collection<E> listAll() {
if (logger.isDebugEnabled()) {
logger.debug("[listAll] Entity<" + this.clazz.getName() + ">.");
}
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listAllOrderBy(boolean, java.lang.String)
*
* @param isAsc 是否使用升序
* @param orderByPropertyName 排序的属性
* @return
*
* 获取所有的Entity的list,结果按照orderByPropertyName属性升序/降序(isAsc true则升序,否则降序)
*/
@SuppressWarnings("unchecked")
public Collection<E> listAllOrderBy(final boolean isAsc,
final String orderByPropertyName) {
final Class clazztemp = this.clazz;
logger.debug("[listAllOrderBy] Entity<" + this.clazz.getName()
+ ">, isAsc?=" + isAsc + ", orderByPropertyName="
+ orderByPropertyName + ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria.addOrder(isAsc ? Order
.asc(orderByPropertyName) : Order
.desc(orderByPropertyName));
releaseSession(session);
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByPage(int, int)
*
* @param pagesize 页的大小,每页最多Entity个数
* @param pageno 页码
* @return
*
* 获取一页的Entity的list,该页是所有Entity中的第pageno页,每页最多有pagesize个Entity,分页显示Entity时使用
*/
@SuppressWarnings("unchecked")
public Collection<E> listByPage(final int pagesize, final int pageno) {
final Class clazztemp = this.clazz;
logger.debug("[listByPage] Entity<" + this.clazz.getName()
+ ">, pagesize=" + pagesize + ", pageno=" + pageno + ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria.setFirstResult(pagesize * pageno);
criteria.setMaxResults(pagesize);
releaseSession(session);
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByPageOrderBy(int, int, boolean,
* java.lang.String)
*
* @param pagesize 页的大小,每页最多Entity个数
* @param pageno 页码
* @param isAsc 是否使用升序
* @param orderByPropertyName 排序的属性
* @return
*
* 获取一页的Entity的list,该页内容按照orderByPropertyName属性升序/降序(isAsc true则升序,否则降序),该页是所有Entity中的第pageno页,每页最多有pagesize个Entity
* 分页显示排序过的Entity时使用
*/
@SuppressWarnings("unchecked")
public Collection<E> listByPageOrderBy(final int pagesize,
final int pageno, final boolean isAsc,
final String orderByPropertyName) {
final Class clazztemp = this.clazz;
logger.debug("[listByPageOrderBy] Entity<" + this.clazz.getName()
+ ">, pagesize=" + pagesize + ", isAsc?=" + isAsc
+ ", orderByPropertyName" + orderByPropertyName
+ ", pageno=" + pageno + ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria
.setFirstResult(pagesize * pageno)
.setMaxResults(pagesize)
.addOrder(
isAsc ? Order.asc(orderByPropertyName)
: Order
.desc(orderByPropertyName));
releaseSession(session);
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByProperty(java.lang.String,
* java.lang.Object)
*
* @param propertyName Entity属性的名称
* @param value Entity属性的值
* @return
*
* 获取一组具有相同propertyName属性值(value)的Entity的列表list,返回一组符合条件的Entity
*/
@SuppressWarnings("unchecked")
public Collection<E> listByProperty(String propertyName, Object value) {
if (logger.isDebugEnabled()) {
logger.debug("[listByProperty] Entity<" + this.clazz.getName()
+ ">, propertyName" + propertyName + ", value=" + value
+ ".");
}
"from " + this.clazz.getName() + " table where table."
+ propertyName + "=?", value);
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByPageByProperty(java.lang.String,
* java.lang.Object, boolean, java.lang.String)
*
* @param pagesize 页的大小,每页最多Entity个数
* @param pageno 页码
* @param listByPropertyName Entity属性的名称
* @param value Entity属性的值
* @return
*
* 获取一页的Entity的list,该页内容为具有相同propertyName属性值(value)的Entity,该页是所有符合条件Entity中的第pageno页,每页最多有pagesize个Entity
* 分页显示匹配某一属性值的Entity时使用
*/
@SuppressWarnings("unchecked")
public Collection<E> listByPageByProperty(final int pageno,
final int pagesize, final String listByPropertyName,
final Object value) {
final Class clazztemp = this.clazz;
logger.debug("[listByPageByProperty] Entity<"
+ this.clazz.getName() + ">, pageno=" + pageno
+ ", pagesize=" + pagesize + ", listByPropertyName="
+ listByPropertyName + ", value" + value + ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria.setFirstResult(pagesize * pageno)
.setMaxResults(pagesize).add(
Restrictions.eq(listByPropertyName,
value));
releaseSession(session);
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByPropertyOrderBy(java.lang.String,
* java.lang.Object, boolean, java.lang.String)
*
* @param listByPropertyName Entity属性的名称
* @param value Entity属性的值
* @param isAsc 是否使用升序
* @param orderByPropertyName 排序的属性
* @return
*
* 获取所有的具有匹配propertyName属性值(value)的Entity的list,按照orderByPropertyName属性升序/降序(isAsc true则升序,否则降序)
* 显示匹配属性值的列表且要求排序时使用
*/
@SuppressWarnings("unchecked")
public Collection<E> listByPropertyOrderBy(final String listByPropertyName,
final Object value, final boolean isAsc,
final String orderByPropertyName) {
final Class clazztemp = this.clazz;
logger.debug("[listByPropertyOrderBy] Entity<"
+ this.clazz.getName() + ">, listByPropertyName="
+ listByPropertyName + ", value" + value + ", isAsc?="
+ isAsc + ", orderByPropertyName=" + orderByPropertyName
+ ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria
.add(Restrictions.eq(listByPropertyName, value))
.addOrder(
isAsc ? Order.asc(orderByPropertyName)
: Order
.desc(orderByPropertyName));
releaseSession(session);
}
});
}
* @see com.goldnet.dao.IDAO.IGenericDAO#listByPageByPropertyOrderBy(int, int,
* java.lang.String, java.lang.Object, boolean, java.lang.String)
*
* @param pagesize 页的大小,每页最多Entity个数
* @param pageno 页码
* @param listByPropertyName Entity属性的名称
* @param value Entity属性的值
* @param isAsc 是否使用升序
* @param orderByPropertyName 排序的属性
* @return
*
* 获取一页的Entity的list,该页内容为具有相同propertyName属性值(value)的Entity,该页内容按照orderByPropertyName属性升序/降序(isAsc true则升序,否则降序),
* 该页是所有符合条件Entity中的第pageno页,每页最多有pagesize个Entity
* 分页显示匹配某一属性值的Entity时使用
*/
@SuppressWarnings("unchecked")
public Collection<E> listByPageByPropertyOrderBy(final int pagesize,
final int pageno, final String listByPropertyName,
final Object value, final boolean isAsc,
final String orderByPropertyName) {
final Class clazztemp = this.clazz;
logger.debug("[listByPageByPropertyOrderBy] Entity<"
+ this.clazz.getName() + ">, pageno=" + pageno
+ ", pagesize=" + pagesize + ", listByPropertyName="
+ listByPropertyName + ", value" + value + ", isAsc?="
+ isAsc + ", orderByPropertyName=" + orderByPropertyName
+ ".");
}
new HibernateCallback() {
public Object doInHibernate(Session session) {
Criteria criteria = session.createCriteria(clazztemp);
criteria
.setFirstResult(pagesize * pageno)
.setMaxResults(pagesize)
.add(Restrictions.eq(listByPropertyName, value))
.addOrder(
isAsc ? Order.asc(orderByPropertyName)
: Order
.desc(orderByPropertyName));
releaseSession(session);
}
});
}
}
生活的效率,累的是谁?
Webwork 2.2的form tag在使用jsp view时onsubmit不工作
起因是使用Michael Chen的JSValidation,需要手动写onsubmit,结果居然发现webwork 2.2的正式版本里面的form tag里面的onsubmit在使用jsp view的时候没法输出,苦恼于此。暂时通过修改模板修改了一下,但是如此很不爽。所以就想测试一下到底是不是我们项目的问题还是ww 2.2的问题。但一直太忙,今天才抽出功夫测。以下是测试纪录,希望知道缘由的朋友帮我指点一下。
测试使用Webwork发行包中演示的starter项目,其中使用了ftl模板作为表现层,我按它的方式制造了newPerson.jsp。
下面是两个页面的源文件:
newPerson.jsp
java代码: |
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="webwork" prefix="ww"%> <html> <head> <title>New Person</title> <link href="<ww:url value="/webwork/jscalendar/calendar-blue.css" />" rel="stylesheet" type="text/css" media="all"/> </head> <body> <ww:submit value="Create person"/> </ww:form> </body> </html> |
newPerson.ftl
java代码: |
<html>
<head> <title>New Person</title> <link href="<@ww.url value="/webwork/jscalendar/calendar-blue.css" />" rel="stylesheet" type="text/css" media="all"/> </head> <body> </body> </html> |
程序逻辑继续使用starter项目里面的内容。
xwork中配置如下:
java代码: |
<action name="newPerson" class="com.acme.CreatePerson">
<result name="success" type="redirect">listPeople.action</result> <!– <result name="input" type="dispatcher">newPerson.jsp</result> –> <result name="input" type="freemarker">newPerson.ftl</result> </action> |
两个"success"的result轮流切换。
试验结果如下:
1、webwork.properties配置如下,使用vm的template,此时使用两种result的结果是相同的:
java代码: |
webwork.ui.theme=simple
webwork.ui.templateDir=template/archive webwork.ui.templateSuffix=vm |
显示结果,显然onsubmit没有输出,此时datapicker标签没有输出(因为老的vm模板没有提供datapicker标签):
java代码: |
<form namespace="/" id="newPerson" name="newPerson" action="/starter/newPerson.action">
|
2、webwork.properties配置如下,此时使用ftl的result和ftl的template:
java代码: |
webwork.ui.theme=simple
webwork.ui.templateDir=template webwork.ui.templateSuffix=ftl |
显示结果,onsubmit输出正常:
java代码: |
<form id="newPerson" name="newPerson" onsubmit="dosomething" action="/starter/newPerson.action">
|
3、webwork.properties配置如下,此时使用jsp的result和ftl的template:
java代码: |
webwork.ui.theme=simple
webwork.ui.templateDir=template webwork.ui.templateSuffix=ftl |
显示结果,onsubmit没有输出:
java代码: |
<form namespace="/" id="newPerson" name="newPerson" action="/starter/newPerson.action">
|
此时尝试使用theme="xhtml"还是没有输出。
从上面的测试来看,似乎是个bug,可是在webwork网站却没有查到相关issue,怀疑是我这里的环境问题。以上测试在tomcat 5.5.12 or Weblogic 9.1 @ SUN JDK 1.5b6上面进行。希望哪位解决了上面问题指导一下我,谢谢。
Spring+Hibernate中使用Mysql存储过程初步
Hibernate中使用Mysql存储过程
1、我使用了mysql-connector-java-5.0.0-beta-bin.jar(其实用老的mysql-connector-java-3.1.8-bin.jar也可以调用存储过程的)这个最新的mysql驱动。
2、数据库我使用了mysql-5.0.18-win32,安装后建立了一个简单的数据表。
sql如下:
java代码: |
create database testprocedure;
use testprocedure; create table testtable (id int(11) AUTO_INCREMENT, content varchar(255), readcount int(11) DEFAULT 0,primary key (id)); desc testtable;(查看是否建立正确) |
3、建立一个专用的用户(可选,建立时请使用具有grant权限的用户如root):
java代码: |
grant select,delete,update,create,alter,execute on testtable.* to testprocedure@"localhost" identified by "test";
|
用户名为testprocedure,密码test。注意权限中的execute,它就是执行call procedure的权限。在你的Hibernate配置中使用该帐户。
4、建立一个存储过程:
sql如下:
java代码: |
delimiter //
(注意//是新的命令结束符,方便我们建立procedure) create procedure readcountplusone (inputid int) begin update testtable set readcount = readcount + 1 where id = inputid; end// (建立存储过程完毕) delimiter ; (恢复命令结束符为;) |
5、测试一下存储过程:
java代码: |
insert into testtable values (null,‘test’,0);
select * from testtable; call readcountplusone(1); select * from testtable; |
应该看到原先readcount为0,call以后变成1,而且每次call都加1。
如果执行有错,可以删除procedure重新建立。
删除的命令为drop procedure readcountplusone;
6、开始在我们的Hibernate+Spring support项目中使用procedure:
HBM映射我们不说了,这里没有使用named query。Hibernate+Spring的配置这里也不多说了,应该可以搜寻到很多文章。
我的DAO是extends HibernateDAO,具体的使用方法可以参照其他很多讲Spring hibernate support的文章。
我们建立一个方法,比较丑陋(只是测试,大家有好方法可以提),假设对应testtable的pojo为TestPojo,它的getId()返回id对应的值:
java代码: |
public void readCountPlusOne(final TestPojo pojo) {
getHibernateTemplate().execute(new HibernateCallback() { public Object doInHibernate(Session session) { try { Connection conn = session.connection(); String sql = "{call readcountplusone(?)}"; e.printStackTrace(); } } return null; |
7、然后我们在我们的bussiness中调用readCountPlusOne方法既可实现通过Hibernate调用一个简单的Mysql存储过程。
有点走马观花,主要是把口舌都放在mysql部分,hibernate部分则用的比较简单,我想把调用方法改为Named Query,这样不会这么丑陋。
抛砖引玉,谢谢。
2006.1.16 工作总结
我们使用的JSValidation升级为1.0b4以后就不工作了,我本想放弃。后来下午灵机一动,把新的1.0b4用UTF-8转存一下就OK了,靠!而且同时发现我们使用的已经是1.0b4了,我去年就……靠靠!
发现比较有趣的地方:
1、Webwork封装的sessionMap其实就是Map,使用起来完全相同。使用map.put map.get即可,自己放什么类型就是什么类型。
2、Webwork的OGNL里面自动set回来的parameter是String[]类型,遍历时使用((String[])session.get("somekey"))[0]即可访问到,注意先检测是否contains那个key。原理是由于html的form的name允许重复,所以返回的可能是parameter或者parameters,后者就是一个数组,所以OGNL的setter会自动把你的结果全部以数组方式保存。直接使用就有点麻烦了,不过无所谓了。所以webwrok里面ModelDriven风格才是推荐。
所以我们的需要使用Session的UserAuthenticationManagerjiu只需map.get即可。
而后就是漫长的实现,发现了global-results,这个需要配置在package里面。发现它可以覆盖,即在子package的global-results会覆盖父package的global-results,方便。
不过要注意,在package里面,global-results要在action前面声明。
然后还发现一个非常重大的Bug,或者说问题,是关于Spring的:
在配置的时候使用模版方式定义,即:
<bean id="basicActionWithAuthtication" abstract="true">
此时该bean里面的所有注入属性都可以工作,也就是parent指向basicActionWithAuthtication的bean都会继承它的ref bean。
但是在该bean里面声明的singleton="false"却不会被继承。
这个需要参考以下参考书了,比较重要,不能误解!
在Spring reference的第26-27页(1.2.6)第3.5节Abstract and child bean definitions:
A child bean definition will inherit constructor argument values, property values and method overrides from the
parent, with the option to add new values. If init method, destroy method and/or static factory method are
specified, they will override the corresponding parent settings.
The remaining settings will always be taken from the child definition: depends on, autowire mode, dependency
check, singleton, lazy init.
意思就是一般构造参数和参数值及方法都会从parent继承和覆盖,而如果指定了init方法destroy方法和static factory方法,也会覆盖parent的相应配置。
但是depends on、autowire mode、dependency check、singleton、lazy这些属性则不会被继承,都直接从child中获取。
这也就解释了我们使用的parent里面设置singleton="false"为什么不起作用(Spring默认true,其实就是我们的parent设置不会被继承)。
里面还有个说明有意思:
对于没有制定abstract="true"的action,如果它的class没有指定,则它默认情况下也是一个abstract。但是对于那些指定了class的bean,如果不指定abstract="false",则默认它会初始化。但是对于无abstract声明,但由没有class制定的bean,被引用或者初始化,则会报错!所以要注意,考虑好你的class是否是abstract,声明好。个人人为还是纯抽象并作为模版比较好,表示配置相同的那种方式(都指定class,且有parent关系)则不太合适。
这个的严重性会造成Webwork的action变成singleton,如此的话,两台机器同时调用/administrator/load.action就会调出同一个admin,太可怕了!
java中的String的encoding和下载文件的jsp例子
晚上跟zh等同志一起看了下文件下载的中文问题,问题解决,并且收获不小:
1、java的char类型是16bit,而不是c/c++中的8bit,所以char和byte之间的转换会造成严重的精度损失。而Java的char是Unicode的。
2、java中String是基于char[]的,也就是说这个String天生就可以表示Unicode。所以如果从byte[]创建一个String就会涉及到一个严重的策略问题,是一个byte转一个String字还是两个byte转一个String字。
3、所以我们常用的java转码中的语句somestr = new String(somestr.getBytes(),"ISO-8859-1")与somestr = new String(somestr.getBytes(),"GB2312")之间是有很大区别的,它们关系到从一个byte变两个byte或反过来。
4、所以,对于单精度的"ISO-8859-1"与双精度的GB2312、GBK、utf-8处理的时候一定要分清,而且它也可以解决很多的问题。而如果你好好了解GB2312与GBK的效果其实很类似,他们只有部分的编码覆盖度上的区别,大部分字符是通用的,可是"ISO-8859-1"却与他们完全不同。
5、还有一个有意思的地方:java.io.Writer与java.io.InputStream下面的东西有个明显的区别,前者接受char[],而后者接受byte[]。其实理解一下,他们之间的转换其实正好需要String的单字节编码与双字节编码的区别,我们可以用它们完成有意思的转换。在它们的沟通过程中我们如果使用buffer,则需要String作为中间者进行转换,算个小trick吧。
6、从这里,我们引出今天解决的中文文件下载的问题中的有意思的地方:
<%@ page language="java" import="java.io.PrintWriter"
%><%
String filename = (String)request.getAttribute("downloadFileName");
String filepath = (String)request.getAttribute("downloadFileUrl");
filename = new String(filename.getBytes(),"ISO-8859-1");
if(filename != null && filepath != null) {
response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + filename + "\"");
java.io.FileInputStream fileInputStream =
new java.io.FileInputStream(filepath);
java.io.File file = new java.io.File(filepath);
response.setContentLength((new Long(file.length()).intValue()));
PrintWriter pw = response.getWriter();
byte[] charArray = new byte[4096];
int len;
while ((len=fileInputStream.read(charArray)) != -1) {
String s = new String(charArray,"ISO-8859-1");
pw.write(s);
}
pw.flush();
pw.close();
fileInputStream.close();
}
%>
webork2与spring的autowire配合的trick
今天,又看了下上面这个例子里面的starter那个例子,发现:
它使用了Spring的自动装配,以下两段代码效果相同:
java代码:
<beans default-autowire="autodetect">
<bean id="personManager" class="com.acme.PersonManager"/>
</beans>
java代码:
<beans>
<bean id="personManager" class="com.acme.PersonManager"/>
<bean id="listPeople" class="com.acme.ListPeople">
<property name="personManager" >
<ref local="personManager" />
</property>
</bean>
<bean id="createPserson" class="com.acme.CreatePerson">
<property name="personManager">
<ref local="personManager" />
</property>
</bean>
</beans>
如果这样使用的话,如果在xwork里面配制好action的class,然后连这个class的bean都不用在spring里面声明了,其依赖也通过名字匹配自动装配了。
真简单啊,想知道spring的autowire有没有性能损耗,如果每次装配都反射会不会性能很差?