|  | 
				
					
			  
	2006年2月6日
	
 
			
			
			1.Oracle 8i 下使用最新的oracle thin driver时用DatabaseMetaData获取主键等信息时,需要将 connection.getMetaData().getPrimaryKeys(connection.getCatalog(),null,tableName); 中的tableName转为大写,否则无法得到数据。 2.正则表达式中,需要以","分割字符串,但是要分割的字串中含有","号,为了避免冲突,引入前置转义字符"\",这样的正则怎么写呢? 例如: String txt = "STATE_COUNTY=kj\\\\,,ADDR_LINE1=l=j,ADDR_LINE2=mj\n\n,ADDR_LINE3=n\\,o,\n\nADDR_LINE4=\np"; 需要把键值对切分出来:  Pattern.compile("[^\\\\],)");这个是不行的,会将","号前一个字符消耗掉。  Pattern.compile("(?![\\\\]),)");也不行  Pattern p = Pattern.compile,",(?![\\\\])");倒是可以,但是把转义字符放后面似乎有点诡异。 找了一个折衷办法,不切割使用正则获取"键=值"子串:  Pattern p = Pattern.compile("\\w+\\s*=.*?[,]*.*?(?=,|$)",Pattern.DOTALL);但是还是带来了子串中不能含有"="的问题。 最后查了一个JDK1.4 DOC,发现了一个反向的非匹配串写法:  Pattern p = Pattern.compile("(?<!\\\\),\\s*");这样一来就解决了以上问题。
			 
			
			
			
		
				乱弹权限系统续一
				原文在这:http://www.blogjava.net/RongHao/archive/2006/07/03/56258.html
 
 仔细分析一,二,三,四权限背后的实质可以发现:
 一系统权限的概念有一些冗余,很难想象这样一种情况:你已经有了子系统下的很多权限,结果因为没有模块权限而使得无法使用该模块进行任何操作,分配权限的人要非常小心才行.这个世界已经够复杂了,不要再给开发,部署人员增加复杂度了.很明白的,这个权限是不需要资源的权限
 二数据库操作权限的概念,有一点疑惑,不知道为什么要建立这样的一个概念,和行级权限有什么区别呢? 从你的上下文理解来看,似乎是这样子的:有操作X表的业务,如果用户有增加权限,则可以任意增加数据,如果用户有编辑权限,则可以编辑任意数据.实际上对应标准权限模型为:不需要限定资源的操作,即不需要资源标识的权限.
 三行级数据权限,这个概念很直白,对应标准权限模型就是: 资源(行数据)+操作
 四列级数据权限,由于不是针对某特定行数据,所以它也是无资源型权限
 就这样,所有的权限最终可划为需要资源标识和不需要资源标识,换句话说,所有权限可划分为控制某些集合的权限和控制单体的权限两种,在某些时候,也称之为 功能权限和数据权限
 
				谈到把权限分给别人,很自然的就是如何控制权限的权限的问题了,很拗口,是吧?仔细想想,这样很直观,也没有什么后遗症,权限自递归控制和自解释,真是一个完美的循环.
 有爱思考的同学想深了,会觉得非常麻烦,难实现.当然,概念上一回事,具体实现上可以是另一回事,可以做很多的变通来达到目的.只要保持概念上的简单性,就足以使得非常多的人得以解脱了。
 另外,作为架构设计者,非常非常不赞成动辄就把很底层的概念扯进高层设计中(例如行级,数据库什么的),很容易把自己和别人搞胡涂。可以最近状态不好,要不好好blog一篇,8过,有句话怎么说来着:“都素那浮云而已。。。”
 
			
			
			     摘要: 在本篇文章中,作者在一个系统的构建中深度地被各种配置逻辑所困扰,由此发现了IOC工具(如Spring,Nuts等)的又一个发展方向。  阅读全文 
			
			
			
		需求: 某机构体系下,机构类型分为子公司,部门,人员等,以后可能在某机构或者其子孙机构下可能会再分出其他子机构类型,希望在增加新类型过程中,尽可能的避免修改已有代码。 情况:子公司,部分,人员等已完成所有编码(界面,商业逻辑,数据逻辑)变化:需要把这个机构体系组成为一颗树状结构
 策略:鉴于除了树结构外的其他部分代码已经完成,那么应该首先保持这些代码不予改动。复用修改的优先级从高到低的顺序如下:
 界面×JSP,Action层
 商业逻辑 Service层
 数据逻辑层
 数据物理层
 有经验的人知道,大部分情况下,越是下层的改动,越是影响越广泛(注意不是修改难度),所以我们只有在无计可施的情况下,才进行低层的修改。
 分析: 回到我们的需求,从功能上看,维护一个组织机构的需求,已经涵盖了每一个子结构的维护需求,以部门的建立为例,在新建一个部门时,同时也必须建立机构树上的节点,这样,如果需要直接使用原有的创建部门的所有代码,需要在其上加上创建组织机构所需要的父节点,以及当前节点名称信息(在这里department的增加界 面JSP是需要修改的,不过实际上我没有修改该文件,而是利用DHTML来动态加入需要新增加的信息),然后提交给原创建部门的URI (departmentSave.action)和组织机构创建URI(orgCreate.action),在这里我们利用ww提供的action chain功能来完成这两个操作。
 这里需要修改department.action的配置,拦截save方法使其执行完后跳过原来的relist结果页面转向组织结构的创建orgCreate.action:
 <action name="unitSave" class="com.wolfsquare.ibase.org.action.UnitAction" method="save">
 <result name="input">/org/unit/input.jsp</result>
 <result name="relist" type="chain">
 <param name="actionName">orgCreate</param>
 <param name="namespace">/org</param>
 </result>
 <result name="xxx" type="redirect">/org/unit.action?start=${start}</result>
 <interceptor-ref name="validationStack"/>
 </action>
 可能有同学看到这里会问:创建组织节点时应该还需要关联前面创建的部门对象啊,这个操作是如何实现的?信息是如何传递的?
 在这里,由于整个架构体系并没有支持这种信息传递的功能,所以只好以一种比较”脏“的方式实现:
 在department.action类里增加了一个方法getModel()返回刚刚创建的部门对象,然后在org.action类中增加一个接收的方法setModel(object o)这样在整action chain执行的时候,ww会自动将getModel后的数据填入setModel中,这样做的后果是以后增加新的机构类型的功能时,action必须也照这样的语意设置getModel方法。(如果要解决这个问题,这能需要使用一个特定的Context,然后拦截指定Service的创建方法,把创建结果放入Context,不过这又带来如何清除Context的问题,于是又要求助与ww的interspector,专门写一个拦截器来擦屁股,够麻烦。。。)
         就这样,我们完成了新增,修改组织机构的功能合成,虽然有点拖沓,但是还是达到了复用,少修改原有代码,而且扩展性也很好的目标。这上篇说的是两个简单业务的功能揉合问题,下篇我们来看看稍微复杂点的情况,看看还能不能继续依葫芦画瓢来完成功能合的成
 (未完待续)
 
 
			
			
			虽然以前一直在用log4j,但是对其配置不甚了了,突然间因为需解决某些问题,要理解log4j的配置, 然而用google搜了一下,却发现网上没有一个简单直观的说明,于是只好看log4j的官方介绍,终于 理解了log4j的配置用法,以下是我对log4j配置的一点认识,如有谬误还请不吝赐教.
 首先我们搞清楚log4j能干什么,简单来说就是提供一个记录不同级别信息内容的日志工具,可以把不同级别,不同包路径的信息,以指定格式输出到多种设备(控制台,文件等)
 在程序中,可以以以下方式来使用
 Log log = org.apache.commons.logging.LogFactory.LogFactory.getLog(yourClassName.class);
 log.debug("debug message -------------------");
 log.info("info message ******************");
 log.warn("warn message +++++++++++++++");
 log.error("error msg=================");
 
 本文主要讲的是如何配置log4j,先让我们先看看一个典型的log4j配置:
 ==========log4j.properties================== log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH\:mm\:ss.SSS} %-5p [%F\:%L]%x %m%n
 log4j.appender.fileout=org.apache.log4j.RollingFileAppenderlog4j.appender.fileout.File=D:/workspace/log4jtest/log/application.log
 log4j.appender.fileout.MaxFileSize=10000KB
 log4j.appender.fileout.MaxBackupIndex=10
 log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
 log4j.appender.fileout.layout.ConversionPattern=%d{MM-dd HH:mm:ss.SSS}[%24F:%-3L:%-5p]%x %m%n
 log4j.rootCategory=INFO, stdout, fileoutlog4j.logger.com.wolfsquare.log2=DEBUG,stdout
 ===================================
 这个文件可以划为三小块 ===========第一块定义了一个名为 stdout 的appender和layout (appender,layout的概念后面再解释,目前先记着有这样两个名词): log4j.appender.stdout=org.apache.log4j.ConsoleAppender定义stdout的实际输出实现类,从这个appender实现类名可以猜到,这个类是负责控制台输出的。
 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
 定义stdout的输出装饰器
 log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd HH\:mm\:ss.SSS} %-5p [%F\:%L]%x %m%n
 装饰器参数配置
 ============第二块定义了一个名为 fileout 的appender和layout:
 log4j.appender.fileout=org.apache.log4j.RollingFileAppender
 同理可猜这个实现类是输出到文件的
 log4j.appender.fileout.File=D:/workspace/log4jtest/log/application.log
 log4j.appender.fileout.MaxFileSize=10000KB
 log4j.appender.fileout.MaxBackupIndex=10
 log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
 log4j.appender.fileout.layout.ConversionPattern=%d{MM-dd HH:mm:ss.SSS}[%24F:%-3L:%-5p]%x %m%n
 ============第三块定义了名字分别为rootCategory,log4j.logger.com.wolfsquare.log2的两个loggerlog4j.rootCategory=INFO, stdout, fileout
 log4j.logger.com.wolfsquare.log2=DEBUG,stdout
 rootCategory logger是缺省的logger,记录所有的包的信息输出。第二个logger是只输出指定包com.wolfsquare.log2下的日志信息。
 那么INFO,DEBUG又是什么意思呢,他们是信息的分级标识,通过继承实现这个实现自定义级别的分级。
 第三块配置两句的意思是这样的:
 rootCategory 把所有类的INFO级别以上的信息输出到stdout和fileout两个appender中,
 logger.com.wolfsquare.log2,把com.wolfsquare.log2包中的所有类(包括子包)DEBUG级别(含)以上的信息输出到stdout 中
 一个logger可以输出到很多个设备中(appender),如果需要增加输出设备则用分号分隔开appender名称即可。
 输出信息的分类级别是DEBUG > INFO > WARN > ERROR,信息细节由细到粗,指定输出某一级别的信息时,过细的信息输出将会被忽略
 如果一个配置中有多个logger,他们之间会有什么关系呢?答案是,在输出上,他们没有任何关系,都是独立运作的,不相关的,但是在配置上,父包的配置会传给子包,如果子包没有另外定义配置的话。
 例如上面配置文件中的两个logger:
 log4j.logger.com.wolfsquare
 log4j.logger.com.wolfsquare.log2
 这里认为 log4j.logger.com.wolfsquare.log2 继承自 log4j.logger.com.wolfsquare,他们的配置声明如下:log4j.rootCategory=INFO, stdout, fileout
 log4j.logger.com.wolfsquare.log2=,stdout
 注意第二句没有指定输出级别,那么根据配置继承规则会继承父logger的配置,在这里就是INFO。
 同时需要强调的是,如果两个logger有继承关系,且输出到同一个appender,根据输出独立原则,那么将会出现两行一样的信息,例如上面的两个logger定义会导致这样的情况。
 最后以一幅图来概括:
 
  
			
			
			问题:Spring+Hibernate的应用中,定义了两个业务Service,这里分别称它们为serivceA,ServiceB。它们的关系简单点来说是这样的:
 serviceA需要引用serviceB,在serviceB中定义了一个接口列表,serverA必须在serviceB初始化时设置进列表。
 在纯bean的情况下,也就是这两个类不需要设置其他bean的情况下,循环引用是正常的,可以通过的。例如下面配置所表示:
     <bean id="serviceA" class="A"  autowire="byName"  lazy-init="true"><property name="serviceB"><ref local="serviceB"/></property>
 </bean>
 <bean id="serviceB" class="B"  autowire="byName"  lazy-init="true">
 <property name="serviceA"><ref bean="serviceA"/></property>
 </bean>
 但是作为一个业务接口,它应该是不需要关心事务,回滚这些无关的东西,
 但现实又有这样的需求,所以我们必须保证透明的实现这个功能,于是引
 入了AOP方式解决该问题,利用的是Spring自带的org.springframework.t
 ransaction.interceptor.TransactionProxyFactoryBean.
 重新声明文件如下:
 <bean id="baseTxProxy" lazy-init="true"
 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
 <property name="proxyTargetClass"><value>true</value></property>
 <property name="transactionAttributes">
 <props>
 <prop key="*">PROPAGATION_REQUIRED</prop>
 </props>
 </property>
 </bean>
 
 <bean id="serviceA" parent="baseTxProxy">
 <property name="target"><ref local="serviceAImpl"/></property>
 </bean>
 
 <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
 <property name="serviceB">
 <ref bean="serviceB"/>
 </property>
 </bean>
 
 <bean id="serviceB" parent="baseTxProxy" lazy-init="true">
 <property name="target"><ref local="serviceBImpl"/></property>
 </bean>
 
 <bean id="serviceBImpl" class="D" lazy-init="true">
 <property name="serviceA">
 <ref bean="serviceA"/>
 </property>
 </bean>
 于是问题就出现了,Spring报了FactoryBeanCircularReferenceException,无法继续完成设置工作。
 查看TransactionProxyFactoryBean源码,其实现了FactoryBean和InitializingBean接口,应该是
 做了代理之后,两个代理Bean需要等待所有Bean设置完成后才会标识状态为初始化完毕,于是造成了
 冲突。
     由于两个业务服务互相调用的路径是不相交的,所以采用了一种变通的方法,在声明serviceA时,直接定义serviceB:
 <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
 <property name="serviceB">
 <bean class="B"  autowire="byName"/>
 </property>
 </bean>
 相当于serviceB和serviceA中使用的serviceB不是同一个实例。
 
 但是如果确实调用重合时怎么办?
 
 解决方法是这样的:
 
 <bean id="serviceAImpl" class="serviceA"  autowire="byName"  lazy-init="true">
 <property name="serviceB">
 <ref bean="serviceBImpl"/>
 </property>
 </bean>
 
 非常简单,serviceAImpl调用时,可能已经在事务环境中了,不需再使用serviceB代理的事务支持,
 于是直接引用serviceB实例。这个方法是我写这篇文章时想到的,-_-!!!,看来知识果真还是好好
 整理呀。
   
			
			
			环境Spring1.1.3,Hibernate 2.1.8, Websphere5.01 hbm文件采用通配符获取: 
 
 classpath:/**/*.hbm.xml
 
 
 
 问题症状: 应用启动报错说不能重复定义某类,去掉该类后仍然报下一个类重复定义。 仔细查看Log输出发现,所有的hbm文件均找到了两份 -_-!!! 项目组认为应该是websphere不太厚道,在classpath中使用了多处目录(web-inf & classes),并以这些目录为根进行递归搜索匹配文件,可是如果这些目录有包含关系,WebSphere就没有处理重复查找的文件了。 于是在以上配置中改为: 
 
 classpath:/classes/**/*.hbm.xml
 
 
 
 问题虽然解决了,可是tomcat中却又无效了。 :( 什么时候,企业应用才能一次拷贝,到处运行啊~~ 
			
			
			问题1:JDBC Sql Server varchar的取出最大长度限制 环境: JDBC驱动inet tds驱动(版本不明),SQLServer2K 问题症状:对于数据库声明为varchar的长度大于256的字段,可以正常保存,但是无法取出多于256字符以后的内容 问题2:使用Hibernate映射时0长度字符串保存后,取出多加了一个空格 环境:inet tds驱动Hibernate2.1.8,SQL Server2K 问题症状:保存0长度字符串后,取出增加了多余的空格。 以上两个问题都是因为没有使用最新的通讯协议引起的,修改URL声明方式如下: jdbc:inetdae7:127.0.0.1:1433?database=xxx 问题解决,收工。 ps:发现协议inetdae时,数据库字段为Null时,Hibernate取出声明为基本类型(例如boolean)的对象属性并不会报错,实际上在其他数据库如Oracle和新协议上是会报错的。为了避免此类问题出现,最好还是严格遵守:Hibernate声明对象的基本类型属性,一定不能在数据库端置为空值。 ps2:在解决以上问题中发现,Oracle居然对传人0长度字符串,会转为空值,不知道是为了节省空间还是别的什么理由。-_-!!! (全文完) 
			
			
			    在Java环境中,可以使用 java.awt.Toolkit.getScreenResolution()可以得到屏幕每英寸的象素数,但是好像没有什么方法能知道某一台打印机的分辨率,更别提去控制打印粒度了。于是可耻的使用着丑陋的缺省打印精度几年后,终于找到了解决方法,不知道该高兴还是悲伤,其原理说出来也是非常的简单: 提高打印精度,其实就是把本来是A3纸的内容往A4纸里画,也就是说,打印区域(这里对应着Java里的Graphics对象)需要缩小,然后由于缺省情况下打印是照72DPI来打的,不做改变的话,打印内容也会跟着变小。这样就不是我们想要的效果了,所以还得把打印内容成比例放大。一个缩小,一个放大,于是画完后,在指定大小的纸张内,便容纳了比以往更多象素的内容,这下世界总算完美了。
     以上做法形象的说应该是这样:把需要产生的图形对象先放大,画在一张“纸上”,然后整体缩小,这样精度就提高了。     tips 1:在一般企业报表表格打印中,使用144DPI得到的表格线的宽度看起来最舒服。tips 2:现在号称600DPI的打印机其实是576DPI,如果想使用这个分辨率的精度,需要用好一点的纸张,因为已经到极限了,纸张稍差点,打印墨粉就沾不上,导致线体残缺。
 附源码(修改分辨率就改动变量iResMul就好):    import java.awt.*; 
  import java.awt.print.*; 
  
   public class MyPrintableObject implements Printable  { 
  public int iResMul = 1; // 1 = 72 dpi; 4 = 288 dpi  
  
  public int print(Graphics g, PageFormat pf, int iPage) 
   throws PrinterException  { 
  final int FONTSIZE = 12; 
  final double PNT_MM = 25.4 / 72.; 
  if (0 != iPage) 
  return NO_SUCH_PAGE; 
   try  { 
  int iPosX = 1; 
  int iPosY = 1; 
  int iAddY = FONTSIZE * 3 / 2 * iResMul; 
  int iWdth = (int) Math.round(pf.getImageableWidth() * iResMul) - 3; 
  int iHght = (int) Math.round(pf.getImageableHeight() * iResMul) - 3; 
  int iCrcl = Math.min(iWdth, iHght) - 4 * iResMul; 
  Graphics2D g2 = (Graphics2D) g; 
  PrinterJob prjob = ((PrinterGraphics) g2).getPrinterJob(); 
  g2.translate(pf.getImageableX(), pf.getImageableY()); 
  g2.scale(1.0 / iResMul, 1.0 / iResMul); 
  g2.setFont(new Font("SansSerif", Font.PLAIN, FONTSIZE * iResMul)); 
  g2.setColor(Color.black); 
  g2.drawRect(iPosX, iPosY, iWdth, iHght); 
  g2.drawLine(iPosX, iHght / 2 + iWdth / 50, iPosX + iWdth, iHght / 2 
  - iWdth / 50); 
  g2.drawLine(iPosX, iHght / 2 - iWdth / 50, iPosX + iWdth, iHght / 2 
  + iWdth / 50); 
  g2.drawOval(iPosX + 2 * iResMul, iHght - iCrcl - 2 * iResMul, 
  iCrcl, iCrcl); 
  iPosX += iAddY; 
  iPosY += iAddY / 2; 
  g2.drawString("PrinterJob-UserName: " + prjob.getUserName(), iPosX, 
  iPosY += iAddY); 
  g2.drawString("Betriebssystem: " + System.getProperty("os.name") 
  + " " + System.getProperty("os.version"), iPosX, 
  iPosY += iAddY); 
  g2 
  .drawString("Java-Version: JDK " 
  + System.getProperty("java.version"), iPosX, 
  iPosY += iAddY); 
  g2.drawString("Width/Height: " + dbldgt(pf.getWidth()) + " / " 
  + dbldgt(pf.getHeight()) + " points = " 
  + dbldgt(pf.getWidth() * PNT_MM) + " / " 
  + dbldgt(pf.getHeight() * PNT_MM) + " mm", iPosX, 
  iPosY += iAddY); 
  g2.drawString("Imageable Width/Height: " 
  + dbldgt(pf.getImageableWidth()) + " / " 
  + dbldgt(pf.getImageableHeight()) + " points = " 
  + dbldgt(pf.getImageableWidth() * PNT_MM) + " / " 
  + dbldgt(pf.getImageableHeight() * PNT_MM) + " mm", iPosX, 
  iPosY += iAddY); 
  g2.drawString("Imageable X/Y: " + dbldgt(pf.getImageableX()) 
  + " / " + dbldgt(pf.getImageableY()) + " points = " 
  + dbldgt(pf.getImageableX() * PNT_MM) + " / " 
  + dbldgt(pf.getImageableY() * PNT_MM) + " mm", iPosX, 
  iPosY += iAddY); 
  g2.drawString("versuchte Druckaufl sung: " + 72 * iResMul + " dpi", 
  iPosX, iPosY += iAddY); 
   } catch (Exception ex)  { 
  throw new PrinterException(ex.getMessage()); 
  } 
  return PAGE_EXISTS; 
  } 
  
   private static double dbldgt(double d)  { 
  return Math.round(d * 10.) / 10.; // show one digit after point 
  } 
  
   public static void main(String[] args)  { 
  PrinterJob pj = PrinterJob.getPrinterJob(); 
  pj.setPrintable(new MyPrintableObject()); 
   if (pj.printDialog())  { 
   try  { 
  pj.print(); 
   } catch (PrinterException e)  { 
  System.out.println(e); 
  } 
  } 
  } 
  } 
  
 (全文完)
 |