tbwshc

2012年7月5日 #

Java调用SQL Server的存储过程详解

1使用不带参数的存储过程

  1

  {call procedure-name}

  作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:

  CREATE PROCEDURE GetContactFormalNames

  AS BEGIN SELECT TOP 10 Title + ' ' + FirstName + ' ' + LastName AS FormalName

  FROM Person.Contact END

  此存储过程返回单个结果集,其中包含一列数据(由 Person.Contact 表中前十个联系人的称呼、名称和姓氏组成)。

  在下面的实例中,将向此函数传递 AdventureWorks 示例tb数据库的打开连接,然后使用 executeQuery 方法调用 GetContactFormalNames 存储过程。

  public static void executeSprocNoParams(Connection con)

  …{

  try …{

  Statement stmt = con.createStatement();

  ResultSet rs = stmt.executeQuery("{call dbo.GetContactFormalNames}");

  while (rs.next())

  …{

  System.out.println(rs.getString("FormalName"));

  }

  rs.close();

  stmt.close();

  }

  catch (Exception e)

  …{

  e.printStackTrace();

  }

  }

  2使用带有输入参数的存储过程

  使用 JDBC 驱动程序调用带参数的存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 IN 参数的 call 转义序列的语法如下所示:

  {call procedure-name[([parameter][,[parameter]]…)]}http://jie.baijiale.94ibc.com

  构造 call 转义序列时,请使用 ?(问号)字符来指定 IN 参数。此字符充当要传递给该存储过程的参数值的占位符。可以使用 SQLServerPreparedStatement 类的 setter 方法之一为参数指定值。可使用的 setter 方法由 IN 参数的数据类型决定。

  向 setter 方法传递值时,不仅需要指定要在参数中使用的实际值,还必须指定参数在存储过程中的序数位置。例如,如果存储过程包含单个 IN 参数,则其序数值为 1.如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2.

  作为如何调用包含 IN 参数的存储过程的实例,使用 SQL Server 2005 AdventureWorks 示例数据库中的 uspGetEmployeeManagers 存储过程。此存储过程接受名为 EmployeeID 的单个输入参数

posted @ 2013-10-24 17:05 chen11-1 阅读(615) | 评论 (0)编辑 收藏

Java中生成文件的10项建议

1. 记住 - "越少越好"并非总是如此(Keep in Mind – "Less is more" is not always better)。 – 高效率的代码是件好事,但很多情况下,并非代码行数越少效率就越高

  2. 不要把简单事情复杂化(Do not complicate things)。 – 我曾经这么做过,我相信你也一样。开发者都倾向于采用复杂方式解决简单问题。我们在一个只有5个用户的系统中引入EJB,为一个并不需要框架的应用实现一套框架,采用属性文件、采用面向tb对象解决方案、使用线程,而这些根本用不着。为什么会这么做?一些人可能不知道有更好的解决方案,但另一些人可能故意这样做来学习新知识,或仅仅是因为有趣。对那些不知道更好解决方案的人,要多听有经验程序员的建议。对于那些纯粹出于个人目的而将设计复杂化的人,我建议你要更加专业一点。
  3. 不要"硬编码"(No hard coding please)。 – 由于时间紧迫,开发者总是会忘记或故意忽略这一条。然而另一种可能是,遵循这条戒律,我们就不会陷入"时间紧迫"的困境。定义一个static final 变量,增加一行代码,又能花多长时间呢?

  4. 为代码添加注释(Add comments to your code)。 – 每个人都知道这一点,但不是每个人都会这么做。你有多少次"忘记"添加注释了?确实,注释不会为你的程序增加任何函数功能。但是,有多少次,看到2周前写的代码,你都记不起它是干什么的?你很幸运,那些未注释的代码是你自己写的,你脑海中还会有残存的印象。非常不幸,大多时候,代码是别人写的,并且那个人很可能已经离开公司了。有句谚语说的好:"有来有往,互惠互利",因此程序员应该体谅彼此(还有你自己),给你的代码加上注释。

 

posted @ 2013-10-24 17:04 chen11-1 阅读(312) | 评论 (0)编辑 收藏

针对Java Excel API及详细教程

时在java开发中会操作excel表格,其实操作起来也特别简单。这里把前期操作步骤说一下,本文会简单的介绍一个开放源码项目:Java Excel Api,使用它大家就可以方便的操作Excel文件了。

  首先下载好:Java Excel Api,这个文件我已经和 JAVA+Excel+API详细教程。pdf一并压缩上传了,感兴趣的朋友可以下载!

  我这里用的开发平台是Eclipse,这里我把操作简单说一下:

  1, 建,立java项目,在这个项目在建立一个新的文件夹lib;

  2, 将jxl.jar,即Java Excel Ap,复制到lib

  3,然后右键点击这个java项目,tb选择Propertieshttp://jie.baijiale.ibc198.com

  4,在左侧列表里选中Java Build Path ,右侧选中Libraries

  5,点击Add JARs

  6, 然后去选择这个项目中lib文件夹中的jxl.jar,点击确定

  成功后,项目中会多一个文件夹为:Referenced Libraries

  准备工作完成后,就可以去操作excel了,

posted @ 2013-10-24 17:02 chen11-1 阅读(276) | 评论 (0)编辑 收藏

框架Quart在Java中任务调度的使用

  Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。

  核心概念

  Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节

  Job任务:其实Job是接口,其中只有一个execute方法:

  package org.quartz;

  public abstract interface Job

  {

  public abstract void execute(JobExecutionContext paramJobExecutionContext)

  throws JobExecutionException;

  }

  我们开发者只要实现此接口,实现execute方法即可。把我们想做的事情,在execute中执行即可。

  JobDetail:任务细节,Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。

  Trigger触发器:执行任务的规则;比如每天,每小时等。

  一般情况使用SimpleTrigger,和CronTrigger,这个触发器实现了Trigger接口。

  对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger

  对于简单的时间来说,比如每天执行几次,使用SimpleTrigger

  scheduler任务调度:是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。

  注意:

  不同的版本的jar包,具体的操作不太相同,但是tbw思路是相同的;比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联。SimpleTrigger和CornTrigger是类;在2.0.2jar包中,JobDetail是个接口,SimpleTrigger和CornTrigger是接口

  不同版本测试:

  1.8.6jar包:

  [html]

  package com.test;

  import java.util.Date;

  import org.quartz.Job;

  import org.quartz.JobExecutionContext;

  import org.quartz.JobExecutionException;

  /**

  * 需要执行的任务

  * @author lhy

  *

  */

  public class MyJob implements Job {

  @Override

  //把要执行的操作,写在execute方法中

  public void execute(JobExecutionContext arg0) throws JobExecutionException {

  System.out.println("测试Quartz"+new Date());

  }

  }

  package com.test;

  import java.util.Date;

  import org.quartz.Job;

  import org.quartz.JobExecutionContext;

  import org.quartz.JobExecutionException;

  /**

  * 需要执行的任务

  * @author lhy

  *

  */

  public class MyJob implements Job {

  @Override

  //把要执行的操作,写在execute方法中

  public void execute(JobExecutionContext arg0) throws JobExecutionException {

  System.out.println("测试Quartz"+new Date());

  }

  }

  使用SimpleTrigger触发器

  [html]

  package com.test;

  import java.util.Date;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleTrigger;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class SchedulerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类

  JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);

  // 定义调度触发规则,比如每1秒运行一次,共运行8次

  SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger","triggerGroup");

  // 马上启动

  simpleTrigger.setStartTime(new Date());

  // 间隔时间

  simpleTrigger.setRepeatInterval(1000);

  // 运行次数

  simpleTrigger.setRepeatCount(8);

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(jobDetail, simpleTrigger);

  // 启动调度

  scheduler.start();

  }catch(SchedulerException e){

  e.printStackTrace();

  }

  }

  }

  package com.test;

  import java.util.Date;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleTrigger;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class SchedulerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类
 JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);

  // 定义调度触发规则,比如每1秒运行一次,共运行8次

  SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger","triggerGroup");

  // 马上启动

  simpleTrigger.setStartTime(new Date());

  // 间隔时间

  simpleTrigger.setRepeatInterval(1000);

  // 运行次数

  simpleTrigger.setRepeatCount(8);

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(jobDetail, simpleTrigger);

  // 启动调度

  scheduler.start();

  }catch(SchedulerException e){

  e.printStackTrace();

  }

  }

  } 若使用CornTrigger触发器:

  [html]

  package com.test;

  import java.util.Date;

  import org.quartz.CronTrigger;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleTrigger;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class CronTriggerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类

  JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);

  // 定义调度触发规则,每天上午10:15执行

  CronTrigger cornTrigger=new CronTrigger("cronTrigger","triggerGroup");

  // 执行规则表达式

  cornTrigger.setCronExpression("0 15 10 * * ? *");

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(jobDetail, cornTrigger);

  // 启动调度

  scheduler.start();

  }catch(Exception e){

  e.printStackTrace();

  }

  }

  }

  package com.test;

  import java.util.Date;

  import org.quartz.CronTrigger;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleTrigger;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class CronTriggerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类

  JobDetail jobDetail=new JobDetail("job1", "jgroup1", MyJob.class);

  // 定义调度触发规则,每天上午10:15执行

  CronTrigger cornTrigger=new CronTrigger("cronTrigger","triggerGroup");

  // 执行规则表达式

  cornTrigger.setCronExpression("0 15 10 * * ? *");

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(jobDetail, cornTrigger);

  // 启动调度

  scheduler.start();

  }catch(Exception e){

  e.printStackTrace();

  }

  }

  }

  对于2.0.2jar包如下:

  其中的job类不变,主要是调度类如下:

  [html]

  package com.test;

  import java.util.Date;

  import org.quartz.CronScheduleBuilder;

  import org.quartz.JobBuilder;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleScheduleBuilder;

  import org.quartz.Trigger;

  import org.quartz.TriggerBuilder;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class SchedulerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类

  JobDetail job=JobBuilder.newJob(MyJob.class).withIdentity("job1", "jgroup1").build();

  // 定义调度触发规则

  // 使用simpleTrigger规则

  // Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")

  // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8))

  // .startNow().build();

  // 使用cornTrigger规则 每天10点42分

  Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")

  .withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *"))

  .startNow().build();

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(job, trigger);

  // 启动调度

  scheduler.start();

  }catch(Exception e){

  e.printStackTrace();

  }

  }

  }

  package com.test;

  import java.util.Date;

  import org.quartz.CronScheduleBuilder;

  import org.quartz.JobBuilder;

  import org.quartz.JobDetail;

  import org.quartz.Scheduler;

  import org.quartz.SchedulerException;

  import org.quartz.SchedulerFactory;

  import org.quartz.SimpleScheduleBuilder;

  import org.quartz.Trigger;

  import org.quartz.TriggerBuilder;

  import org.quartz.impl.StdSchedulerFactory;

  /**

  * 调用任务的类

  * @author lhy

  *

  */

  public class SchedulerTest {

  public static void main(String[] args) {

  //通过schedulerFactory获取一个调度器

  SchedulerFactory schedulerfactory=new StdSchedulerFactory();

  Scheduler scheduler=null;

  try{

  // 通过schedulerFactory获取一个调度器

  scheduler=schedulerfactory.getScheduler();

  // 创建jobDetail实例,绑定Job实现类

  // 指明job的名称,所在组的名称,以及绑定job类

  JobDetail job=JobBuilder.newJob(MyJob.class).withIdentity("job1", "jgroup1").build();

  // 定义调度触发规则

  // 使用simpleTrigger规则

  // Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")

  // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8))

  // .startNow().build();

  // 使用cornTrigger规则 每天10点42分

  Trigger trigger=TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup")

  .withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *"))

  .startNow().build();

  // 把作业和触发器注册到任务调度中

  scheduler.scheduleJob(job, trigger);

  // 启动调度

  scheduler.start();

  }catch(Exception e){

  e.printStackTrace();

  }

  }

  }

  上述demo下载:1.8版本demo下载

  2.0版本demo下载

  对于CornExpress讲解如下:

  字段 允许值 允许的特殊字符

  秒 0-59 , - * /

  分 0-59 , - * /

  小时 0-23 , - * /

  日期 1-31 , - * ? / L W C

  月份 1-12 或者 JAN-DEC , - * /

  星期 1-7 或者 SUN-SAT , - * ? / L C #

  年(可选) 留空, 1970-2099 , - * /

  表达式 意义

  "0 0 12 * * ?" 每天中午12点触发

  "0 15 10 ? * *" 每天上午10:15触发

  "0 15 10 * * ?" 每天上午10:15触发

  "0 15 10 * * ? *" 每天上午10:15触发

  "0 15 10 * * ? 2005" 2005年的每天上午10:15触发

  "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发

  "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发

  "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

  "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发

  "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发

  "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发

  "0 15 10 15 * ?" 每月15日上午10:15触发

  "0 15 10 L * ?" 每月最后一日的上午10:15触发

  "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发

  "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发

  "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

  特殊字符 意义

  * 表示所有值;

  ? 表示未说明的值,即不关心它为何值;

  - 表示一个指定的范围;

  , 表示附加一个可能值;

  / 符号前表示开始时间,符号后表示每次递增的值;

  L("last") ("last") "L" 用在day-of-month字段意思是 "这个月最后一天";用在 day-of-week字段, 它简单意思是 "7" or "SAT"。 如果在day-of-week字段里和数字联合使用,它的意思就是 "这个月的最后一个星期几" – 例如: "6L" means "这个月的最后一个星期五". 当我们用“L”时,不指明一个列表值或者范围是很重要的,不然的话,我们会得到一些意想不到的结果。

  W("weekday") 只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16天即周一触发;如果这个月第15天是周二,那么就在tbw触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day-of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。

  # 只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用"6#3"指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。

  C 指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天

posted @ 2013-09-17 15:15 chen11-1 阅读(2011) | 评论 (0)编辑 收藏

序列化在IO中读写对象的使用

    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

  序列化的实现:将需要被序列化的类实现Serializable接口,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
 写对象和读对象的时候一定要使用序列化:

  import java.io.*;

  class Product implements Serializable {

  private static final long serialVersionUID = 1L;

  private float price;

  private float tax;

  public Product(float price) {

  this.price = price;

  tax = (float)(price*0.20);

  }

  public String toString() {

  return "price:"+price+",tax:"+tax;

  }

  }

  public class CmdDemo {

  public static void main(String[] strtb) throws Exception {

  Product p1 = new Product(100);

  ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream

  ("d:\product.txt"));

  os.writeObject(p1);

  os.close();

  ObjectInputStream is = new ObjectInputStream(new FileInputStream

  ("d:\product.txt"));

  Product p2 = (Product) is.readObject();

  System.out.println(p2.toString());

  }

  }

posted @ 2013-09-17 15:12 chen11-1 阅读(261) | 评论 (0)编辑 收藏

Java数组使用实用的技巧

  本文分享了关于Java数组最顶级的11大方法,帮助你解决工作流程问题,无论是运用在团队环境或是在私人项目中,你都可以直接拿来用!

0.  声明一个数组(Declare an array)
 
String[] aArray = new String[5];
String[] bArray = {"a","b","c", "d", "e"};
String[] cArray = new String[]{"a","b","c","d","e"};

1.  在Java中输出一个数组(Print an array in Java)
 
int[] intArray = { 1, 2, 3, 4, 5 };
String intArrayString = Arrays.toString(intArray);
 
// print directly will print reference value
System.out.println(intArray);
// [I@7150bd4d
 
System.out.println(intArrayString);
// [1, 2, 3, 4, 5]

2. 从数组中创建数组列表(Create an ArrayList from an array)
 
String[] stringArray = { "a", "b", "c", "d", "e" };
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));
System.out.println(arrayList);
// [a, b, c, d, e]

3. 检查爱淘宝数组中是否包含特定值(Check if an array contains a certain value)
 
String[] stringArray = { "a", "b", "c", "d", "e" };
boolean b = Arrays.asList(stringArray).contains("a");
System.out.println(b);
// true
 
4. 连接两个数组( Concatenate two arrays)
 
int[] intArray = { 1, 2, 3, 4, 5 };
int[] intArray2 = { 6, 7, 8, 9, 10 };
// Apache Commons Lang library
int[] combinedIntArray = ArrayUtils.addAll(intArray, intArray2);
5. 声明一个数组内链(Declare an array inline )
 
method(new String[]{"a", "b", "c", "d", "e"});
 
6. 将数组元素加入到一个独立的字符串中(Joins the elements of the provided array into a single String)

// containing the provided list of elements
// Apache common lang
String j = StringUtils.join(new String[] { "a", "b", "c" }, ", ");
System.out.println(j);
// a, b, c
 
7. 将数组列表转换成一个数组 (Covnert an ArrayList to an array)
 
String[] stringArray = { "a", "b", "c", "d", "e" };
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));
String[] stringArr = new String[arrayList.size()];
arrayList.toArray(stringArr);
for (String s : stringArr)
    System.out.println(s);
 
8. 将数组转换成一个集合(Convert an array to a set)
 
Set<String> set = new HashSet<String>(Arrays.asList(stringArray));
System.out.println(set);
//[d, e, b, c, a]
 
9. 反向数组(Reverse an array)
 
int[] intArray = { 1, 2, 3, 4, 5 };
ArrayUtils.reverse(intArray);
System.out.println(Arrays.toString(intArray));
//[5, 4, 3, 2, 1]
 
10. 删除数组元素(Remove element of an array)
 
int[] intArray = { 1, 2, 3, 4, 5 };
int[] removed = ArrayUtils.removeElement(intArray, 3);
//create a new array
System.out.println(Arrays.toString(removed));
 
One more – convert int to byte array
 
byte[] bytes = ByteBuffer.allocate(4).putInt(8).array();
 
for (byte t : bytes) {
   System.out.format("0x%x ", t);
}

posted @ 2013-09-17 15:08 chen11-1 阅读(255) | 评论 (0)编辑 收藏

Java动态代理设计模式

 所谓动态代理类是在运行时生成的class,在生成它时,你必须提供一组interface给它,则动态代理类就宣称它实现了这些interface。当然,动态代理类就充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
  下面通过实例来说明:
  Subject.java 抽象借口:声明代理对象和真实对象的共同接口
  [java]
  public interface Subject {
  public void doSomething();
  }
  public interface Subject {
  public void doSomething();
  }
  RealSubject.java 真实被tb代理对象
  [java]
  public class RealSubject implements Subject {
  @Override
  public void doSomething() {
  System.out.println("RealSubject.doSomething");
  }
  }
  public class RealSubject implements Subject {
  @Override
  public void doSomething() {
  System.out.println("RealSubject.doSomething");
  }
  }

  DynamicProxy.java 代理对象
  [java]
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  public class DynamicProxy implements InvocationHandler {
  private Object object;
  public DynamicProxy(Object object) {
  this.object = object;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
  System.out.println("Before Invoke ! method : " + method);
  //我们可以再代理方法调用前后添加功能
  Object result = method.invoke(object, args);
  System.out.println("object : " + object + " result : " + result + " args : " + args);
  System.out.println("After Invoke !");
  return result;
  }
  }
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  public class DynamicProxy implements InvocationHandler {
  private Object object;
  public DynamicProxy(Object object) {
  this.object = object;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
  System.out.println("Before Invoke ! method : " + method);
  //我们可以再代理方法调用前后添加功能
  Object result = method.invoke(object, args);
  System.out.println("object : " + object + " result : " + result + " args : " + args);
  System.out.println("After Invoke !");
  return result;
  }
  }
  Client.java 测试
  [java]
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Proxy;
  public class Client {
  public static void main(String[] args) throws Exception {
  //创建目标对象,也就是被代理对象
  RealSubject realSubject = new RealSubject();
  //将目标对象交给代理
  InvocationHandler handler = new DynamicProxy(realSubject);
  // Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader()
  // , new Class[]{Subject.class});
  // Subject subject = (Subject)proxyClass.getConstructor(new Class[]{InvocationHandler.class})
  // .newInstance(new Object[]{handler});
  //返回代理对象,相当于上面两句
  Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
  realSubject.getClass().getInterfaces(),
  handler);
  //叫代理对象去doSomething(),其实在代理对象中的doSomething()中还是会
  //用handler来调用invoke(proxy, method, args) 参数proxy为调用者subject(this),
  //method为doSomething(),tb参数为方法要传入的参数,这里没有
  subject.doSomething();
  }
  }
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Proxy;
  public class Client {
  public static void main(String[] args) throws Exception {
  //创建目标对象,也就是被代理对象
  RealSubject realSubject = new RealSubject();
  //将目标对象交给代理
  InvocationHandler handler = new DynamicProxy(realSubject);
  // Class proxyClass = Proxy.getProxyClass(Subject.class.getClassLoader()
  // , new Class[]{Subject.class});
  // Subject subject = (Subject)proxyClass.getConstructor(new Class[]{InvocationHandler.class})
  // .newInstance(new Object[]{handler});
  //返回代理对象,相当于上面两句
  Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
  realSubject.getClass().getInterfaces(),
  handler);
  //叫代理对象去doSomething(),其实在代理对象中的doSomething()中还是会
  //用handler来调用invoke(proxy, method, args) 参数proxy为调用者subject(this),
  //method为doSomething(),参数为方法要传入的参数,这里没有
  subject.doSomething();
  }
  }
  打印结果:
  Before Invoke ! method : public abstract void Subject.doSomething()
  RealSubject.doSomething
  object : RealSubject@ec6b00 result : null args : null
  After Invoke !
  注意:
  Java动态代理涉及到的两个类:
  InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object[] args);参数proxy指代理类,method表示被代理的方法,args为method中的参数数组,返回值Object为代理实例的方法调用返回的值。这个抽象方法在代理类中动态实现。
  Proxy:所有动态代理类的父类,提供用于创建动态代理类和实例的静态方法。

posted @ 2013-09-10 17:08 chen11-1 阅读(261) | 评论 (0)编辑 收藏

30条有用的 Java 编程规则

 (1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字 母。例如:
  ThisIsAClassName
  thisIsMethodOrFieldName
  若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。
  Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu 等,全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。
  (2) 为了常规用途而创建一个类时,请采取“tb经典形式”,并包含对下述元素的定义:
  equals()
  hashCode()
  toString()
  clone()(implement Cloneable)
  implement Serializable
  (3) 对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若 进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
  (4) 应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。 (5) 设计一个类时,请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。
  (6) 使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:
  ■一个复杂的开关语句:考虑采用“多形”机制
  ■数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现
  ■许多成员变量在特征上有很大的差别:考虑使用几个类 。
  (7) 让一切东西都尽可能地“私有”——private。可使库的某一部分“公共化”(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有private字段才能在非同步使用的情况下受到保护。
  (8) 谨惕“巨大对象综合症”。对一些习惯于顺序编程思维、且初涉OOP领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的 对象里。根据编程原理,对象表达的应该是应用程序的概念,而非应用程序本身。
  (9) 若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。
  (10) 任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作(参见第14章14.1.2小节的“用内部 类改进代码”)。
  (11) 尽可能细致地加上释,并用javadoc注释文档语法生成自己的程序文档。
  (12) 避免使用“魔术数字”,这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“100”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。
  (13) 涉及构建器和异常的时候,通常希望重新丢弃在构建器中捕获的任何异常——如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个 对象已正确地创建,从而盲目地继续。
  (14) 当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除,并已丢弃了从RuntimeException继承的一个类(如果还没有的话),从而指出一个编程错误。在采取象这样的方案之前,请确定 finalize()能够在自己的系统中工作(可能需要调用System.runFinalizersonExit(true),从而确保 这一行为)。
  (15) 在一个特定的作用域内,若一个对象必须清除(非由tb垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有 finally从句的try块,开始清除工作。
  (16) 若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。
  (17) 创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编 译期进行类型检查的好处。此外,为使用它们,数组的接收者也许并不需要将对象“造型”到数组里。
  (18) 尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个 interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。
  (19) 在构建器内部,只进行那些将对象设为正确状态所需的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程 中产生不可预知的结果(参见第7章的详细说明)。
  (20) 对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。
  (21) 在现成类的基础上创建新类时,请首先选择“新建”或“创作”。只有自己的设计要求必须继承时,才应考虑这方面的问题。若在本来允许新建的场 合使用了继承,则整个设计会变得没有必要地复杂。
  (22) 用继承及方法覆盖来表示行为间的差异,而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色,这是绝对应该避免 的:应直接使用一个“颜色”字段。

  (23) 为避免编程时遇到麻烦,请保证在自己类路径指到的任何地方,每个名字都仅对应一个类。否则,编译器可能先找到同名的另一个类,并报告出错消 息。若怀疑自己碰到了类路径问题,请试试在类路径的每一个起点,搜索一下同名的.class文件。
  (24) 在Java 1.1 AWT 中使用事件“适配器”时,特别容易碰到一个陷阱。若覆盖了某个适配器方法,同时拼写方法没有特别讲究,最后的结果就是新添加一个方法,而不是覆盖现成方法。然而,由于这样做是完全合法的,所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。
  (25) 用合理的设计方案消除“伪功能”。也就是说,假若只需要创建类的一个对象,就不要提前限制自己使用应用程序,并加上一条“只生成其中一个 ” 注释。请考虑将其封装成一个“独生子”的形式。若在主程序里有大量散乱的代码,用于创建自己的对象,请考虑采纳一种创造性的方案,将些代码封装起来。
  (26) 警惕“分析瘫痪”。请记住,无论如何都要提前了解整个项目的状况,再去考察其中的细节。由于把握了全局,可快速认识自己未知的一些因素,防 止在考察细节的时候陷入“死逻辑”中。
  (27) 警惕“过早优化”。首先让它运行起来,再考虑变得更快——但只有在自己必须这样做、而且经证实在某部分代码中的确存在一个性能瓶颈的时候, 才应进行优化。除非用专门的工具分析瓶颈,否则很有可能是在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解,而且难于维护。
  (28) 请记住,阅读代码的时间比写代码的时间多得多。思路清晰的设计可获得易于理解的程序,但注释、细致的解释以及一些示例往往具有不可估量的价 值。无论对你自己,还是对后来的人,它们都是相当重要的。如对此仍有怀疑,那么请试想自己试图从联机Java文档里找出有用信息时碰到的挫折,这样或许能 将你说服。
  (29) 如认为自己已进行了良好的分析、设计或者实施,那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家,但可以是来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作,看看是否能找出你一度熟视无睹的问题。采取这种方式,往往能在最适合修改的阶段找出一些关键性的问题,避免产品发行后再解决问题而造成的金钱及精力方面的损失。
  (30) 良好的设计能带来最大的回报。简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当的解决方案。但一旦找到了正确的方法,以后的工作就轻松多了,再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失

posted @ 2013-08-23 16:56 chen11-1 阅读(236) | 评论 (0)编辑 收藏

单多线程Java算法相比较

1进程和线程的概念
  1.1什么是进程
  一个进程就是在一个运行的程序,它有自己独立的内存空间,一组系统资源,每个进程的内部数据和状态都是独立的,例如在window是同时打开多个记事本,虽然它们所运行的程序代码都是一样的,但是所使用的内存空间是独立的,互不干扰.
  1.2什么是线程
  线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈
  1.3进程与线程的区别
  1. 进程:每个进程都有独立的代码和数据空间(进程上下文) ,tb进程切换的开销大.
  2. 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小.
  3. 多进程:在操作系统中,能同时运行多个任务程序.
  4. 多线程:在同一应用程序中,有多个顺序流同时执行.
  1.4线程创建的两种方式
  采用继承Thread类创建线程
  该方法比较简单,主要是通过继承java.lang.Thread类,并覆盖Thread类的run()方法来完成线成的创建.Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为.要创建一个线程,程序员必须创建一个从 Thread 类导出的新类.Thread类中有两个最重要的函数run()和start().
  通过实现Runnable接口创建线程
  该方法通过生成实现java.lang.Runnable接口的类.该接口只定义了一个方法run(),所以必须在新类中实现它.但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数
  public Thread(Runnable target);来实现.
  2 单线程和多线程性能比较
  以使用蒙特卡罗概率算法求π为例,进行单线程和多线程时间比较
  2.1什么是蒙特卡罗概率算法

  蒙特卡罗法(Monte Carlo method)是以概率和统计的理论、方法为基础的一种计算方法,将所求解的问题同一定的概率模型相联系,用电子计算机实现统计模拟或抽样,以获得问题的近似解,故又称统计模拟法或统计试验法. --百度百科
  蒙特卡罗求算法求π
  第一步
  画正方形和内切圆
  第二步
  变换表达式
  正方形面积As=(2R)^2
  圆的面积Ac=πR^2
  Ac/As=(2R)^2/πR^2
  π=4As/Ac
  令P=As/Sc,则π=4P
  第三步
  重复N次实验求平均值
  在正方形区域内随机生成一个点A,若A落在圆区域内,M++
  P=M/N
  π=4P,N的取值越大,π的值越精确
  2.2 java代码实现算法
  N取值为10000万,多线程的数为100,每个线程执行100万次模拟实验
  线程实现
  import java.util.concurrent.CountDownLatch;
  public class ProModel implements Runnable {
  public int N;//随机实验的总次数
  public static int M;//随机点落在圆中的次数
  private int id;
  private final CountDownLatch doneSignal;
  OBJ semaphore;
  public ProModel(int id,CountDownLatch doneSignal,int N,OBJ semaphore2){
  this.id=id;
  this.doneSignal=doneSignal;
  this.N=N;
  this.semaphore=semaphore2;
  M=0;
  }
  public void run(){
  int tempM=0;
  for(int i=0;i
  if(isInCircle()){
  tempM++;
  }
  }
  synchronized (semaphore) {
  add(tempM);
  }
  doneSignal.countDown();//使end状态减1
  }
  public void add(int tempM){
  System.out.println(Thread.currentThread().getName());
  M=M+tempM;
  System.out.println(M);
  }
  //随机产生一个在正方形区域的点,判断它是否在圆中
  public boolean isInCircle(){
  double x=Math.random();
  double y=Math.random();
  if((x-0.5)*(x-0.5)+(y-0.5)*(y-0.5)<0.25)
  return true;
  else
  return false;
  }
  public static int getTotal(){
  return M;
  }
  }
  多线程Main实现
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  public class MutliThread {
  public static void main(String[] args) throws InterruptedException {
  long begin=System.currentTimeMillis();
  int threadSize=100;
  int N=1000000;
  OBJ semaphore = new OBJ();
  CountDownLatch doneSignal = new CountDownLatch(threadSize);
  ProModel[] pros=new ProModel[threadSize];
  //设置特定的线程池,大小为threadSizde
  System.out.println(“begins!”);
  ExecutorService exe = Executors.newFixedThreadPool(threadSize);
  for(int i=0;i
  exe.execute(new ProModel(i+1,doneSignal,N,semaphore));
  try{
  doneSignal.await(); //等待end状态变为0, }catch (InterruptedException e) {
  // TODO: handle exception35
  e.printStackTrace();
  }finally{
  System.out.println(“ends!”);
  System.out.println(4*(float)ProModel.getTotal()/(float)(threadSize*N));
  }
  exe.shutdown();
  long end=System.currentTimeMillis();
  System.out.println(“used time(ms):”+(end-begin));
  }
  }
  class OBJ{}
  单线程Main实现
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  public class SingleThread {
  public static void main(String[] args) {
  long begin=System.currentTimeMillis();
  int threadSize=1;
  int N=100000000;
  OBJ semaphore = new OBJ();
  CountDownLatch doneSignal = new CountDownLatch(threadSize);
  ProModel[] pros=new ProModel[threadSize];
  //设置特定的线程池,大小为5
  System.out.println(“begins!”);
  ExecutorService exe = Executors.newFixedThreadPool(threadSize);
  for(int i=0;i
  exe.execute(new ProModel(i+1,doneSignal,N,semaphore));
  try{
  doneSignal.await(); //等待end状态变为0, }catch (InterruptedException e) {
  // TODO: handle exception35
  e.printStackTrace();
  }finally{
  System.out.println(“ends!”);
  System.out.println(4*(float)ProModel.getTotal()/(float)(threadSize*N));
  }
  exe.shutdown();
  long end=System.currentTimeMillis();
  System.out.println(“used time(ms):”+(end-begin));
  }
  }

posted @ 2013-08-23 16:54 chen11-1 阅读(255) | 评论 (0)编辑 收藏

Arrays.asList方法 学习记录

Arrays工具类提供了一些比较实用的方法,比如sort, binarySearch, fill等。其中还有一个asList方法,此方法能够将一个变长参数或者数组转换成List。
  但是,这个生成的List,它是固定长度的,如果对其进行add或者remove的操作,会抛出UnsupportedOperationException,为什么会这样呢?
  带着疑问,查看一下Arrays的源码,可以得到问题的结果。
  Java代码
  /**
  * Returns a fixed-size list backed by the specified array. (Changes to
  * the returned list "write through" to the array.) This method acts
  * as bridge between array-based and collection-based APIs, in
  * combination with Collection.toArray. The returned list is
  * serializable and implements {@link RandomAccess}.
  *
  *
  This method also provides a convenient way to create a fixed-size
  * list initialized to contain several elements:
  *
  * List stooges = Arrays.asList("Larry", "Moe", "Curly");
  *
  *
  * @param a the array by which the list will be backed.
  * @return a list view of the specified array.
  * @see Collection#toArray()
  */
  public static List asList(T... a) {
  return new ArrayList(a);

  方法asList返回的是new ArrayList(a)。但是,这个ArrayList并不是java.util.ArrayList,它是一个Arrays类中的重新定义的内部类。
  具体的实现如下:
  Java代码
  /**
  * @serial include
  */
  private static class ArrayList extends AbstractList
  implements RandomAccess, java.io.Serializable
  {
  private static final long serialVersionUID = -2764017481108945198L;
  private Object[] a;
  ArrayList(E[] array) {
  if (array==null)
  throw new NullPointerException();
  a = array;
  }
  public int size() {
  return a.length;
  }
  public Object[] toArray() {
  return (Object[])a.clone();
  }
  public E get(int index) {
  return (E)a[index];
  }
  public E set(int index, E element) {
  Object oldValue = a[index];
  a[index] = element;
  return (E)oldValue;
  }
  public int indexOf(Object o) {
  if (o==null) {
  for (int i=0; i
  if (a[i]==null)
  return i;
  } else {
  for (int i=0; i
  if (o.equals(a[i]))
  return i;
  }
  return -1;
  }
  public boolean contains(Object o) {
  return indexOf(o) != -1;
  }
  }
  从这个内部类ArrayList的实现可以看出,它继承了类AbstractList,但是没有重写add和remove方法,没有给出具体的实现。查看一下AbstractList类中对add和remove方法的定义,如果一个list不支持add和remove就会抛出UnsupportedOperationException。
  Java代码
  public abstract class AbstractList extends AbstractCollection implements List {
  /**
  * Sole constructor. (For invocation by subclass constructors, typically
  * implicit.)
  */
  protected AbstractList() {
  }
  /**
  * Appends the specified element to the end of this List (optional
  * operation).
  *
  * This implementation calls add(size(), o).
  *
  * Note that this implementation throws an
  * UnsupportedOperationException unless add(int, Object)
  * is overridden.
  *
  * @param o element to be appended to this list.
  *
  * @return true (as per the general contract of
  * Collection.add).
  *
  * @throws UnsupportedOperationException if the add method is not
  * supported by this Set.
  *
  * @throws ClassCastException if the class of the specified element
  * prevents it from being added to this set.
  *
  * @throws IllegalArgumentException some aspect of this element prevents
  * it from being added to this collection.
  */
  public boolean add(E o) {
  add(size(), o);
  return true;
  }
  /**
  * Inserts the specified element at the specified position in this list
  * (optional operation). Shifts the element currently at that position
  * (if any) and any subsequent elements to the right (adds one to their
  * indices).
  *
  * This implementation always throws an UnsupportedOperationException.
  *
  * @param index index at which the specified element is to be inserted.
  * @param element element to be inserted.
  *
  * @throws UnsupportedOperationException if the add method is not
  * supported by this list.
  * @throws ClassCastException if the class of the specified element
  * prevents it from being added to this list.
  * @throws IllegalArgumentException if some aspect of the specified
  * element prevents it from being added to this list.
  * @throws IndexOutOfBoundsException index is out of range (index <
  * 0 || index > size()).
  */
  public void add(int index, E element) {
  throw new UnsupportedOperationException();
  }
  /**
  * Removes the element at the specified position in this list (optional
  * operation). Shifts any subsequent elements to the left (subtracts one
  * from their indices). Returns the element that was removed from the
  * list.
  *
  * This implementation always throws an
  * UnsupportedOperationException.
  *
  * @param index the index of the element to remove.
  * @return the element previously at the specified position.
  *
  * @throws UnsupportedOperationException if the remove method is
  * not supported by this list.
  * @throws IndexOutOfBoundsException if the specified index is out of
  * range (index < 0 || index >= size()).
  */
  public E remove(int index) {
  throw new UnsupportedOperationException();
  }
  }
  至此,为什么Arrays.asList产生的List是不可添加或者删除,否则会产生UnsupportedOperationException,就可以得到解释了。
  如果我们想把一个变长或者数据转变成List, 而且tb期望这个List能够进行add或者remove操作,那该怎么做呢?
  我们可以写一个类似的方法,里面直接采用java.util.ArrayList即可。
  比如:
  Java代码
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  public class MyArrays {
  public static List asList(T... a) {
  List list = new ArrayList();
  Collections.addAll(list, a);
  return list;
  }
  }
  测试代码如下:
  Java代码
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  public class Test {
  @SuppressWarnings("unchecked")
  public static void main(String[] args) {
  List stooges = Arrays.asList("Larry", "Moe", "Curly");
  print(stooges);
  List> seasonsList = Arrays.asList(retrieveSeasonsList());
  print(seasonsList);
  /*
  * 自己实现一个asList方法,能够添加和删除。
  */
  List list = MyArrays.asList("Larry", "Moe", "Curly");
  list.add("Hello");
  print(list);
  }
  private static void print(List list) {
  System.out.println(list);
  }
  private static List retrieveSeasonsList() {
  List seasonsList = new ArrayList();
  seasonsList.add("Spring");
  seasonsList.add("Summer");
  seasonsList.add("Autumn");
  seasonsList.add("Winter");
  return seasonsList;
  }
  }
  输出结果:
  [Larry, Moe, Curly]
  [[Spring, Summer, Autumn, Winter]]
  [Larry, Moe, Curly, Hello]

posted @ 2013-07-15 17:08 chen11-1 阅读(227) | 评论 (0)编辑 收藏

Java集合框架中几种集合的分析

        集合可以理解为在内存中存放一组对象的容器,对象是数据的封装,而对象又构成了集合。在java中的集合框架是一种线性的数据结构,但是它分为两种,一种是物理结构,一种是逻辑结构。物理结构是一种连续存储的结构,比如说数组;而逻辑结构可以理解为在内存块中是不连续的,比如说链表,一个链表是分为两中内容的,一个是该链表所存储的数据,还有一个呢,那就是指向下一个链表的指针,通过指针而把表给连起来,称之为链表。数组其实也是可以完成像集合一样的存储的,但是数组存在一定的弊端,数组一旦创建,其大小,类型是固定的,在特定的情况下不能更方便的使用,因此,为了与数组互补,集合也就应运而生了。
  在java.util包中存在三种最重要的集合,其分别是list,set,map,它们各自有各自的特点,这三种都是接口。其中list:1> list的实现类主要有ArrayList,Linkedlist及其Vector,list中存储的数据是有序的,而且也是可以重复的,也就是说list按照添加的顺序,依次的tb存储在list下标从小到大的位置。做一个简单的代码测试
  public class Test {
  public static void main(String[] args) {
  Test test = new Test();
  test.testList();
  public void testList(){//类
  //创建一个List对象
  List list = new ArrayList();
  list.add(1);
  list.add(1);
  list.add(2);
  for(Object a:list){
  System.out.print(a+" ");
  }
  }
  此段代码的运行结果为:{1 1 2}
  反应了其有序可重复的特点。
  2>set同样是一个接口,它常用的实现类有Hashset,Treeset。set集合的存储特点可以说是和list完全相反的,它是一种无序而且不能重复的存储特点。同样的用代码做一个测试 public class Test {
  public static void main(String[] args) {
  Test test = new Test();
  test.testSet();
  public void testSet(){
  //创建Set对象
  Set set = new HashSet();
  set.add(1);
  set.add(2);
  set.add(5);
  set.add(3);
  set.add(4);
  set.add(1);
  set.add(null);
  System.out.println(set);
  Iterator it = set.iterator();
  while(it.hasNext()){//判断有元素可迭代
  int i = it.next();
  System.out.print(i+" ");
  }
  }
  }
  }

  此段代码的运行结果是:{1 2 3 4 5}
  可以说明其无序不可重复的特点。
  3>最后则是map集合,map的实现类常用的有Hashmap,Hashtable和Treemap。
  map与以上两者稍微的有点不同,它是一种映射关系,在map中,存储两种数据,tb表达为map,而以上两者都只有一个数据,而且都是通过下标来访问的,map中k是不可以重复的,而v是可以重复的,进行一段代码测试
  public class Test{
  public static void main(String[] args){
  testmap test=new testmap();
  public void testMap(){
  Map map = new HashMap();
  map.put(1, "aaa");
  map.put(2, "bbb");
  map.put(3,"ccc");
  map.put(2, "ddd");
  System.out.println(map);
  //获取key集合(Set)
  Set set = map.keySet();
  Iterator it = set.iterator();
  while(it.hasNext()){
  int key = it.next();
  //通过key获取对应的value值
  String value = map.get(key);
  System.out.println("key="+key+" value="+value);
  }
  }
  }
  }
  此段代码的运行结果是:key=1 value=aaa;key=2 value=ddd;key=3 value=ccc。
  这三种集合各有其所试用的地方,对于像我这种初学者可以让整个代码简化,思路更清晰。
  二:获得各种数据存储方式长度的方法。
  数组:定义一个数组Array[];那么获得该数组长度的方法是使用其length方法。
  字符串: String st = "aaa";
  st.length();
  这里的length是字符串的一种属性,而数组的length是一种方法。
  List:迭代或者直接通过下标输出,list.size()
  set:迭代while(it.hasNext()){//判断有元素可迭代
  int i = it.next();
  System.out.print(i+" ");
  }
  map:只能用迭代的方法,
  //获取key集合(Set)
  Set set = map.keySet();
  Iterator it = set.iterator();
  while(it.hasNext()){
  int key = it.next();
  //通过key获取对应的value值
  String value = map.get(key);
  System.out.println("key="+key+" value="+value);
  }
  map先是通过迭代器先得到key值,因为是一种一一对应的关系,所以用key值就可以得到value值了。

posted @ 2013-07-15 16:52 chen11-1 阅读(241) | 评论 (0)编辑 收藏

hibernate操作数据库的执行步骤

先举个hibernate执行SQL的例子:
  [java] view plaincopy
  public boolean addUser(UserDO userDO) {
  boolean b = false;
  if (userDO != null) {
  try {
  getHibernateTemplate().save(userDO);
  b = true;
  } catch (Exception e) {
  b = false;
  }
  } else {
  b = false;
  }
  return b;
  }
一、hibernate操作数据库的执行步骤,其实很简单,可以自己实现一个小的hibernate框架:
  1、运用java反射机制,获得user对象的类型user.class
  2、参考对象-关系映射元数据(可能是DO的spring注解,可能是hibernate配置文档),找到和user类对应的表为user表,并建立对象域跟表属性的对应
  3、根据以上映射信息,生成SQL语句
  4、通过JDBC API来执行SQL语句
  二、hibernate的核心接口:
  1、Configuration接口:配置hibernate,根启动hibernate,创建sessionfactory对象。hibernate通过configuration实例来获得对象-关系映射文件中的元数据,以及动态的配置hibernate属性,然后创建sessionfactory实例。具体执行过程如下:
  [java] view plaincopy
  Configuration config=new Configuration();
  创建configuration实例,configuration类的构造方法中把applicationContext.xml文档加载到内存,tb读取文档中sessionFactory Bean的配置。然后运用java反射机制,获得userDO对象的类型UserDO。configuration类的addClass方法
  [java] view plaincopy
  config.addClass(userDO.class);
  该方法将hbm.xml文档或者通过hibernate注解形式的“对象-关系映射元数据”读入内存,找到和UserDO类对应的表为USER表,并建立对象域跟表属性的对应。
  2、sessionfactory接口:初始化hibernate,充当数据存储源的代理,创建session对象。一个sessionfactory实例对应着一个数据存储源。重量级,一个数据库只创建一个sessionfactory实例,它需要一个很大的缓存,用来存放预定义的SQL语句及映射的元数据等。
  3、session接口:负责保存、更新、删除、加载和查询对象。session对象不是线程安全的,因此应该避免过多个线程共享一个session实例。当session执行sql时候,session会针对上面生成的对象和关系映射,动态组成sql语句。
  4、transaction:事务管理接口。它对底层的事务接口做了封装。
  5、query:执行数据库查询。query实例封装了一个HQL语句,HQL语句是面向对象的,它引用类名和类的属性。
  三、hibernate的使用步骤:
  1、创建hibernate配置文件
  2、创建持久化类
  3、创建对象-关系映射文件
  4、通过hibernate API编写数据库访问代码

posted @ 2013-06-28 17:01 chen11-1 阅读(1035) | 评论 (0)编辑 收藏

Java内存管理优化笔记

1. 垃圾回收
  JVM运行环境中垃圾对象的定义:
  一个对象创建后被放置在JVM的堆内存(heap)中,当永远不再引用这个对象时,它将被JVM在堆内存(heap)中回收。被创建的对象不能再生,同时也没法通过程序语句释放它们。
  不可到达的对象被JVM视为垃圾对象,JVM将给这些对象打上标记,然后清扫回收它们,并将散碎的内存单元收集整合。
  JVM管理的两种类型的内存:
  堆内存(heap),主要存储程序在运行时创建或实例化的对象与变量。
  栈内存(stack),主要存储程序代码中声明为静态(static)(或非静态)的方法。
  堆内存(heap)通常情况下被分为两个区域:新对象(new object)区域与老对象(old object)区域。
  新对象区域:
  又可细分为Eden区域、From区域与To区域。
  Eden区域保存新创建的对象。当该区域中的对象满了后,JVM系统将做可达性测试,主要任务是检测有哪些对象由根集合出发是不可到达的,这些对象就可被JVM回收,且将所有的活动对象从Eden区域拷到To区域,此时有一些对象将发生状态交换,有的对象就从To区域被转移到From区域,此时From区域就有了对象。
  该过程执行期间,JVM的性能非常低下,会严重影响到正在运行的应用的性能。
  老对象区域:
  在老对象区域中的对象仍有一个较长的生命周期。经过一段时间后,被转入tb老对象区域的对象就变成了垃圾对象,此时它们被打上相应的标记,JVM将自动回收它们。
  建议不要频繁强制系统做垃圾回收,因为JVM会利用有限的系统资源,优先完成垃圾回收工作,致使应用无法快速响应来自用户端的请求,这样会影响系统的整体性能。
  2. JVM中对象的生命周期
  对象的整个生命周期大致分为7个阶段:创建(creation)、应用(using)、不可视(invisible)、不可到达(unreachable)、可收集(collected)、终结(finalized)、释放(free)。
  1) 创建阶段
  系统通过下面步骤,完成对象的创建:
  a) 为对象分配存储空间
  b) 开始构造对象
  c) 递归调用其超类的构造方法
  d) 进行对象实例初始化与变量初始化
  e) 执行构造方法体
  在创建对象时的几个关键应用规则:
  避免在循环体中创建对象,即使该对象占用内存空间不大
  尽量及时使对象符合垃圾回收标准
  不要采用过深的继承层次
  访问本地变量优于访问类中的变量

posted @ 2013-06-28 16:59 chen11-1 阅读(227) | 评论 (0)编辑 收藏

关于Java序列化的一些高级用法

该说的都在注释中说完了。直接给程序吧。
  [java] view plaincopyprint?
  package test.javaPuzzler.p5;
  import java.io.*;
  import java.io.ObjectInputStream.GetField;
  import java.io.ObjectOutputStream.PutField;
  // 转载请注明来自http://blog.csdn.net/sunxing007
  // 一个类实现Serializable来表明自己可以被序列化;
  // 有一点需要特别注意的是:
  // 如果子类实现了Serializable,而父类没有,则父类不会被序列化;
  public class SerializableObject implements Serializable {
  // 生成的序列化版本号会因为编译环境,声明的类名,成员名称和数量的变化而不同;
  // 也就是说这个版本号一定程度上记录着类的定义性的信息,如果类的定义变化了,最好重新生成版本号;
  // 如果新的代码使用了旧的版本号,则在反序列化的时候,可以兼容读取旧类的字节码而不会报错;
  private static final long serialVersionUID = 9038542591452547920L;
  public String name;
  public String password;
  // 如果你不希望某个非静态成员被序列化,可以用transient来修饰它;
  public transient int age;
  // 静态成员不会被序列化,因为序列化保存的是实例的状态信息,而静态成员是类的状态信息;
  public static int version = 1;
  public SerializableObject(String name, String password) {
  this.name = name;
  this.password = password;
  }

// 每个类可以写一个writeObject方法,这个方法将会负责该类自身的序列化过程;
  // 比如对于敏感信息如password,可以加密之后再序列化;
  // 这个过程需要用到PutField,它可以指定哪些域会被序列化,怎么序列化(比如加密);
  // 如果没有定义这个方法,将会调用ObjectOutputStream 的 defaultWriteObject;
  // 你可以注释掉readObject方法,然后运行测试用例来测试密码是否被加密;
  private void writeObject(ObjectOutputStream out) throws IOException {
  PutField putFields = out.putFields();
  putFields.put("name", name);
  // 模拟加密密码
  putFields.put("password", "thePassword:" + password);
  out.writeFields();
  }
  // 每个类可以写一个readObjectb方法,该方法负责该类自身的反序列化过程;
  // 比如对序列化时加密后的密码解密;
  // 这个过程需要用到GetField,他可以具体地读取每个域;或执行解密动作等等;
  // 如果没有定义这个方法,将会调用ObjectInputStream 的 defaultReadObject;
  private void readObject(ObjectInputStream in)
  throws ClassNotFoundException, IOException {
  GetField readFields = in.readFields();
  // 读取到成员的值之后,直接赋给该域,即完成该域的反序列化;
  name = (String) readFields.get("name", "defaultName");
  // 模拟解密密码
  String encPassword = (String) readFields.get("password",
  "thePassword:defaultValue");
  password = encPassword.split(":")[1];
  }
  // 序列化
  // 主要用到ObjectOutputStream;
  public void save() throws IOException {
  FileOutputStream fout = new FileOutputStream("e:obj");
  ObjectOutputStream oout = new ObjectOutputStream(fout);
  oout.writeObject(this);
  oout.close();
  fout.close();
  }
  // 反序列化
  // 主要用到ObjectInputStream
  public static SerializableObject load() throws IOException,
  ClassNotFoundException {
  FileInputStream fin = new FileInputStream("e:obj");
  ObjectInputStream oin = new ObjectInputStream(fin);
  Object o = oin.readObject();
  return (SerializableObject) o;
  }
  @Override
  public String toString() {
  return "name: " + name + ", password: " + password;
  }
  // tb测试用例
  public static void main(String[] args) throws IOException,
  ClassNotFoundException {
  SerializableObject so = new SerializableObject(
  "http://blog.csdn.net/sunxing007", "123456");
  so.save();
  System.out.println(so);
  System.out.println(SerializableObject.load());
  }
  }

posted @ 2013-06-17 16:50 chen11-1| 编辑 收藏

Oracle绝对值函数

1.绝对值:abs()

   select abs(-2) value from dual;

2.取整函数(大):ceil()

   select ceil(-2.001) value from dual;(-2)

3.取整函数(小):floor()

   select floor(-2.001) value from dual;(-3)

4.取整函数(截取):trunc()

   select trunc(-2.001) value from dual; (-2)

5.四舍五入:round()

   select round(1.234564) value from dual;(1.2346)

6.取平方:Power(m,n)

   select power(4,2) value from dual;(16)

7.取平方根:SQRT()

   select sqrt(16) value from dual;(4)

8.取随机数:dbms_random(minvalue,maxvalue)

   select sys.dbms.random.value(0,1) value from dual;

9.取符号:Sign()

   select sign(-3) value from dual;(-)

10,取集合的最大值:greatest(value)

   select greatest(-1,3,5,7,9) value from dual;(9)

11.取集合的最小值:least(value)

   select least(-1,3,5,7,9) value from dual;(-1)

12.处理Null值:nvl(空值,代替值)

   select  nvl(null,10) value from dual;(10)

13.求字符序号:ascii()

    select ascii(a) value from dual;

14.求序号字符:chr()

    select chr(97) value from dual;

15.链接:concat()

    select concat("11","22") value from dual;(1122)

16.获取系统时间:sysdate()

    select sysdate value from dual;

17.求日期

    select trunc(sysdate) from dual;

18.求时间

    select  to_char(sysdate,"hh24:mm:ss") from dual;

19.首字母大写:InitCAP()

    select INITCAP(abc def ghi) value from dual;tbw(Abc Def Ghi)

posted @ 2013-06-06 10:16 chen11-1| 编辑 收藏

一些非常实用的 Android 开发资源

Sapan Diwakar在过去几年间一直从事Android开发工作,同时他也积累了一些非常实用的Android应用开发资源,希望对你有所帮助。


1.  Android Annotations

Android Annotations是一个能够加速Android开发的开源框架,它可以帮助开发者处理一些前后台任务、rest服务、应用类、代码片段等,让开发者专注于真正重要的东西。

2.  ActionBarSherlok

ActionBarSherlock是一个扩展的Android支持库,旨在允许开发者通过一个单一的API,在所有的Android版本中都能够非常方便地使用活动栏设计模式。

3.  Spring Android

Java开发者应该比较熟悉,这是一个针对Android开发的Spring框架。我使用最多的是RestTemplate功能,此外,AndroidAnnotations已经支持Spring Rest Template,使得编写REST客户端更加容易。

4.  URLImageViewHelper

如果你想在应用程序中通过URL来加载远程图像,这是最好的选择。URLImageViewHelper提供了辅助tb类,可以很容易地加载远程图像,同时还提供了图像缓存功能。

5.  SubtlePatterns

SubtlePatterns提供了一些高质量的纹理图案资源,图案都非常适合作为应用背景,并且使用也很简单。你可以点击这里来看我如何在应用中使用这些图案。

6.  Sliding Menu(滑动菜单)

顾名思义,SlidingMenu是一个在Android应用中添加滑动菜单效果的库,尽管网上还有一些其他类似的库,但我发现从性能和易于集成方面,SlidingMenu是最好的。

7.  Android ViewPagerIndicator

一个非常棒的库,用于在一个多页面切换视图中指示当前的页面。

8.  Vogella Android教程

vogella团队几乎已经将Android开发中的所有事情做成了教程,对于开发入门非常有帮助。

9.  Android-PullToRefresh

针对Android的下拉刷新库。

10.  谷歌的Android教程

开始Android开发必备的指南、培训资源。

11.  Commonsware Github库

几款Android应用开发库,可以使你的开发工作更加轻松。

你有什么好的资源,欢迎补充。


posted @ 2013-06-04 16:25 chen11-1 阅读(167) | 评论 (0)编辑 收藏

java 数据库通用操作类

package com.hospital.dao.tools;
 
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.log4j.Logger;
 
/**
 * 数据库操作管理类
 * 
 * @author Harlyhood
 * 
 */
public class DBManager {
 
    // --------------------------------------------------------- Instance
    private static Logger logger = Logger.getLogger(DBManager.class);
    // --------------------------------------------------------- Methods
 
    // 数据库连接对象
    private Connection con;
    // SQL语句对象
    private Statement stmt;
    // 带参数的Sql语句对象
    private PreparedStatement pstmt;
    // 记录集对象
    private ResultSet rs;
    // 数据连接管理(连接池对象)
    private DBConnectionManager dcm = null;
 
    /** ***********************手动设置的连接参数********************************* */
    @SuppressWarnings("unused")
    private static String _DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    @SuppressWarnings("unused")
    private static String _URL = "jdbc:sqlserver://localhost:1433;database=Hospital_AI_DB;characterEncoding=gb2312";
    @SuppressWarnings("unused")
    private static String _USER_NA = "sa";
    @SuppressWarnings("unused")
    private static String _PASSWORD = "";
 
    /** ********************************************************************** */
 
    // 默认构造
    public DBManager() {
    }
 
    /** ****************************************************************************************** */
    /**
     * **************************************** 数据库连接初始化
     * ***********************************
     */
    /** ****************************************************************************************** */
 
    /**
     * 得到一个默认的数据库连接[从 com.hospital.dao.tools.db.properties文件初始化]
     * 
     * @throws Exception
     */
    private void getConnection() {
        logger.info("###############open:::::从默认的配置文件得到一个数据库连接");
        // 获取一个连接tb池管理类的实例
        dcm = DBConnectionManager.getInstance();
        // 得到一个数据库连接
        con = dcm.getConnection("mysql");
 
        try {
            con.setAutoCommit(false);
        } catch (SQLException e) {
 
            e.printStackTrace();
        }
    }
 
    /**
     * 从指定参数得到一个连接对象
     * 
     * @param driver
     * @param url
     * @param user_na
     * @param password
     * @throws Exception
     */
    public void getConnection(String driver, String url, String user_na,
            String password) throws Exception {
        try {
            logger.info("###############open:::::从指定配置中得到一个数据库连接");
            Class.forName(driver);
            con = DriverManager.getConnection(url, user_na, password);
        } catch (ClassNotFoundException ex) {
            logger
                    .info("###############Error[com.hospital.dao.tools.DBManager^^^Method:getConnection^^^Line:81]找不到类驱动类: "
                            + driver);
            throw ex;
        } catch (SQLException ex) {
            logger
                    .info("###############Error[com.hospital.dao.tools.DBManager^^^Method:getConnection^^^Line:81]加载类: "
                            + driver + " 时出现 SQLException 异常");
            throw ex;
        }
    }
 
    /** ****************************************************************************************** */
    /**
     * **************************************** 数据库操作方法
     * ***********************************
     */
    /** ****************************************************************************************** */
 
    /**
     * 执行SQL语句操作(更新数据 无参数)
     * 
     * @param strSql
     *            SQL语句
     * @throws Exception
     */
    public boolean executeUpdate(String strSql) throws SQLException {
        getConnection();
        // getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
        boolean flag = false;
        stmt = con.createStatement();
        logger.info("###############::执行SQL语句操作(更新数据 无参数):" + strSql);
        try {
            if (0 < stmt.executeUpdate(strSql)) {
                close_DB_Object();
                flag = true;
                con.commit();
            }
        } catch (SQLException ex) {
            logger
                    .info("###############Error DBManager Line126::执行SQL语句操作(更新数据 无参数):"
                            + strSql + "失败!");
            flag = false;
            con.rollback();
            throw ex;
        }
        return flag;
 
    }
 
    /**
     * 执行SQL语句操作(更新数据 有参数)
     * 
     * @param strSql
     *            sql指令
     * @param prams
     *            参数列表
     * @return
     * @throws SQLException
     */
    public boolean executeUpdate(String strSql, HashMap<Integer, Object> prams)
            throws SQLException, ClassNotFoundException {
        getConnection();
        // getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
        boolean flag = false;
        try {
            pstmt = con.prepareStatement(strSql);
            setParamet(pstmt, prams);
            logger.info("###############::执行SQL语句操作(更新数据 有参数):" + strSql);
 
            if (0 < pstmt.executeUpdate()) {
                close_DB_Object();
                flag = true;
                con.commit();
            }
        } catch (SQLException ex) {
            logger
                    .info("###############Error DBManager Line121::执行SQL语句操作(更新数据 无参数):"
                            + strSql + "失败!");
            flag = false;
            con.rollback();
            throw ex;
        } catch (ClassNotFoundException ex) {
            logger
                    .info("###############Error DBManager Line152::执行SQL语句操作(更新数据 无参数):"
                            + strSql + "失败! 参数设置类型错误!");
            con.rollback();
            throw ex;
        }
        return flag;
 
    }
 
    /**
     * 执行SQL语句操作(查询数据 无参数)
     * 
     * @param strSql
     *            SQL语句
     * @return 数组对象列表
     * @throws Exception
     */
    public ArrayList<HashMap<Object, Object>> executeSql(String strSql)
            throws Exception {
        getConnection();
        // getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
        stmt = con.createStatement();
        logger.info("###############::执行SQL语句操作(查询数据):" + strSql);
        rs = stmt.executeQuery(strSql);
        con.commit();
        if (null != rs) {
            return convertResultSetToArrayList(rs);
        }
        close_DB_Object();
        return null;
    }
 
    /**
     * 执行SQL语句操作(查询数据 有参数)
     * 
     * @param strSql
     *            SQL语句
     * @param prams
     *            参数列表
     * @return 数组对象列表
     * @throws Exception
     */
    public ArrayList<HashMap<Object, Object>> executeSql(String strSql,
            HashMap<Integer, Object> prams) throws Exception {
        getConnection();
        // getConnection(_DRIVER,_URL,_USER_NA,_PASSWORD);
        pstmt = con.prepareStatement(strSql);
        setParamet(pstmt, prams);
        logger.info("###############::执行SQL语句操作(查询数据):" + strSql);
        rs = pstmt.executeQuery();
        con.commit();
        if (null != rs) {
            return convertResultSetToArrayList(rs);
        }
        return null;
    }
 
    /**
     * 执行存储过程(查询数据 无参数)
     * 
     * @param procName
     *            存储过程名称
     * @return 数组列表对象
     * @throws Exception
     */
    public ArrayList<HashMap<Object, Object>> executeProcedureQuery(
            String procName) throws Exception {
        getConnection();// 获取连接
        String callStr = "{call " + procName + "}";// 构造执行存储过程的sql指令
        CallableStatement cs = con.prepareCall(callStr);
        logger.info("###############::执行存储过程(查询数据):" + procName);
        rs = cs.executeQuery();
        con.commit();
        cs.close();
        close_DB_Object();
        return convertResultSetToArrayList(rs);
    }
 
    /**
     * 执行存储过程(查询数据,带参数)返回结果集合
     * 
     * @param procName
     *            存储过程名称
     * @param parameters
     *            参数对象数组
     * @param al
     *            数组列表对象
     * @return 数组列表对象
     * @throws Exception
     */
    public ArrayList<HashMap<Object, Object>> executeProcedureQuery(
            String procName, Object[] parameters) throws Exception {
        int parameterPoint = 0;
        // 获取存储过程信息列表集合
        ArrayList<HashMap<Object, Object>> procedureInfo = getProcedureInfo(procName);
        // 获取存储过程的完全名称
        String procedureCallName = getProcedureCallName(procName,parameters.length);
        // 获取连接对象
        getConnection();
        // 初始化 存储过程 执行对象
        CallableStatement cs = con.prepareCall(procedureCallName);
        // 参数下标变量
        int index = 0;
        // 获取 存储过程信息列表集合的 迭代器 对象
        Iterator<HashMap<Object, Object>> iter = procedureInfo.iterator();
        // 遍历存储过程信息列表集合
        while (iter.hasNext()) {
            HashMap<Object, Object> hm = iter.next();
 
            parameterPoint++;
            // 如果参数是输入参数 way = 0
            if (hm.get("WAY").equals("0")) {
                // 设置参数到cs
                cs.setObject(parameterPoint, parameters[index]);
                // 参数下标+1
                index++;
            }
        }
        // 释放这个对象,做为第二次使用
        procedureInfo = null;
        logger.info("###############::执行存储过程(查询数据):::::" + procedureCallName);
        rs = cs.executeQuery();
        con.commit();
        procedureInfo = convertResultSetToArrayList(rs);
        cs.close();
        close_DB_Object();
        return procedureInfo;
 
    }
 
    /**
     * 执行存储过程(更新,查询数据[简单查询、非纪录集],返回输出参数[非纪录集])
     * 
     * @param procName
     *            存储过程名称
     * @param parameters
     *            参数对象数组
     * @param os
     *            输出参数对象数组
     * @return 输出参数对象数组
     * @throws Exception
     */
    public Object[] executeProcedureUpdate(String procName, Object[] parameters)
            throws Exception {
        logger.info("------------------------------------------------------------------------------------------------------");
        logger.info(" Run --> executeProcedureUpdate ##############   正在执行 存储过程: " + procName +"   ##############");
        CallableStatement cs = null;
        Object []returnVal = null;
        try {
        // 获取 存储过程 调用全名
        String fullPCallName = getProcedureCallName(procName,parameters.length);
        logger.info(" Run --> executeProcedureUpdate #   存储过程命令: " + fullPCallName +"   #");
        //获取存储过程参数信息
        ArrayList<HashMap<Object, Object>> p_Call_Info_List = getProcedureInfo(procName);
        //获取连接
        getConnection();
        //创建 存储过程 执行对象
        cs = con.prepareCall(fullPCallName);
        //数组下标
        int index = 1;
        //输出参数下标 纪录
        ArrayList<Integer> outPutIndexList = new ArrayList<Integer>();
        logger.info(" Run --> executeProcedureUpdate #   参数个数是: " + parameters.length +"   #");
        for(HashMap<Object,Object> tempHash:p_Call_Info_List)
        {
            if("0".equals(tempHash.get("WAY")))
            {
                //设置输入参数
                cs.setObject(index, parameters[index-1]);
                logger.info(" Run --> executeProcedureUpdate #   输入 Input: 编号:" + index +" 值: "+parameters[index-1]+" 类型: "+parameters[index-1].getClass()+"   #");
            }
            else
            {
                //注册输出参数
                cs.registerOutParameter(index, getDataType(tempHash.get("TYPENAME").toString()));
                //纪录输出参数的下标
                outPutIndexList.add(index);
                logger.info(" Run --> executeProcedureUpdate #   输出 OutPut: 编号:" + index +" 值: "+parameters[index-1]+" 类型: "+parameters[index-1].getClass()+"   #");
            }
            index++;
        }
        logger.info(" Run --> executeProcedureUpdate #   参数设置完毕,正在执行中  :   #");
         
        //-------------------- 执行 -----------------
        if(!cs.execute())
        {
            returnVal = new Object[outPutIndexList.size()];
            logger.info(" Run --> executeProcedureUpdate #   执行成功! :   #");
            //取输 出参数的 返回值
            for(int i = 0 ;i<outPutIndexList.size();i++)
            {
                returnVal[i] = cs.getObject(outPutIndexList.get(i));
                logger.info(" Run --> executeProcedureUpdate #   返回值 "+(i+1)+" "+returnVal[i]+"   #");
            }
            con.commit();//提交
        }
        } catch (Exception e) {
            logger.info(" Run --> executeProcedureUpdate #   执行失败!事务回滚中 :   #");
            con.rollback();
            throw e;
        } 
        logger.info("------------------------------------------------------------------------------------------------------");
        return returnVal;
    }
 
    /** ****************************************************************************************** */
    /**
     * ********************************* 小工具
     * ************************************************
     */
    /** ****************************************************************************************** */
 
    /**
     * 关闭数据对象
     */
    public void close_DB_Object() {
        logger.info("###############close:::::关闭连接对象,语句对象,记录集对象");
        if (null != rs) {
            try {
                rs.close();
            } catch (SQLException ex) {
                rs = null;
            }
        }
        if (null != stmt) {
            try {
                stmt.close();
            } catch (SQLException ex) {
                stmt = null;
            }
        }
        if (null != pstmt) {
            try {
                pstmt.close();
            } catch (SQLException ex) {
                pstmt = null;
            }
        }
        if (con != null) {
            dcm.freeConnection("mysql", con);
        }
    }
 
 
    /**
     * 设置Sql 指令参数
     * 
     * @param p_stmt
     *            PreparedStatement
     * @param pramets
     *            HashMap
     */
    private PreparedStatement setParamet(PreparedStatement p_stmt,
            HashMap<Integer, Object> pramets) throws ClassNotFoundException,
            SQLException {
        // 如果参数为空
        if (null != pramets) {
            // 如果参数个数为0
            if (0 <= pramets.size()) {
                for (int i = 1; i <= pramets.size(); i++) {
                    try {
                        // 字符类型 String
                        if (pramets.get(i).getClass() == Class
                                .forName("java.lang.String")) {
                            p_stmt.setString(i, pramets.get(i).toString());
                        }
                        // 日期类型 Date
                        if (pramets.get(i).getClass() == Class
                                .forName("java.sql.Date")) {
                            p_stmt.setDate(i, java.sql.Date.valueOf(pramets
                                    .get(i).toString()));
                        }
                        // 布尔类型 Boolean
                        if (pramets.get(i).getClass() == Class
                                .forName("java.lang.Boolean")) {
                            p_stmt.setBoolean(i, (Boolean) (pramets.get(i)));
                        }
                        // 整型 int
                        if (pramets.get(i).getClass() == Class
                                .forName("java.lang.Integer")) {
                            p_stmt.setInt(i, (Integer) pramets.get(i));
                        }
                        // 浮点 float
                        if (pramets.get(i).getClass() == Class
                                .forName("java.lang.Float")) {
                            p_stmt.setFloat(i, (Float) pramets.get(i));
                        }
                        // 双精度型 double
                        if (pramets.get(i).getClass() == Class
                                .forName("java.lang.Double")) {
                            p_stmt.setDouble(i, (Double) pramets.get(i));
                        }
 
                    } catch (ClassNotFoundException ex) {
                        throw ex;
                    } catch (SQLException ex) {
                        throw ex;
                    }
                }
            }
        }
        return p_stmt;
    }
 
    /**
     * 转换记录集对象为数组列表对象
     * 
     * @param rs
     *            纪录集合对象
     * @return 数组列表对象
     * @throws Exception
     */
    private ArrayList<HashMap<Object, Object>> convertResultSetToArrayList(
            ResultSet rs) throws Exception {
        logger.info("###############::转换记录集对象为数组列表对象");
        // 获取rs 集合信息对象
        ResultSetMetaData rsmd = rs.getMetaData();
        // 创建数组列表集合对象
        ArrayList<HashMap<Object, Object>> tempList = new ArrayList<HashMap<Object, Object>>();
        HashMap<Object, Object> tempHash = null;
        // 填充数组列表集合
        while (rs.next()) {
            // 创建键值对集合对象
            tempHash = new HashMap<Object, Object>();
            for (int i = 0; i < rsmd.getColumnCount(); i++) {
                // 遍历每列数据,以键值形式存在对象tempHash中
                tempHash.put(rsmd.getColumnName(i + 1).toUpperCase(), rs
                        .getString(rsmd.getColumnName(i + 1)));
            }
            // 第一个键值对,存储在tempList列表集合对象中
            tempList.add(tempHash);
        }
        close_DB_Object();// 关闭相关链接
        return tempList;// 返回填充完毕的数组列表集合对象
    }
 
    /**
     * 从数据库得到tb存储过程信息
     * 
     * @param procName
     *            存储过程名称
     * @return 数组列表对象
     * @throws Exception
     */
    private ArrayList<HashMap<Object, Object>> getProcedureInfo(String procName)
            throws Exception {
        return this.executeSql("select Syscolumns.isoutparam as Way,systypes.name as TypeName from sysobjects,syscolumns,systypes where systypes.xtype=syscolumns.xtype and syscolumns.id=sysobjects.id and sysobjects.name='"
                + procName + "' order by Syscolumns.isoutparam");
    }
 
    /**
     * 从数据库得到存储过程参数个数
     * 
     * @param procName
     *            存储过程名称
     * @return 数组列表对象
     * @throws Exception
     */
    @SuppressWarnings("unused")
    private int getParametersCount(String procName) throws Exception {
        int returnVal = 0;
        for (HashMap<Object, Object> tempHas : this
                .executeSql("select count(*) as RowsCount from sysobjects,syscolumns,systypes where systypes.xtype=syscolumns.xtype and syscolumns.id=sysobjects.id and sysobjects.name='"
                        + procName + "'")) {
            returnVal = Integer.parseInt(tempHas.get("ROWSCOUNT").toString());
        }
        return returnVal;
    }
 
    /**
     * 得到调用存储过程的全名
     * 
     * @param procName
     *            存储过程名称
     * @return 调用存储过程的全名
     * @throws Exception
     */
    private String getProcedureCallName(String procName, int prametCount)
            throws Exception {
        String procedureCallName = "{call " + procName;
        for (int i = 0; i < prametCount; i++) {
            if (0 == i) {
                procedureCallName = procedureCallName + "(?";
            }
            if (0 != i) {
                procedureCallName = procedureCallName + ",?";
            }
        }
        procedureCallName = procedureCallName + ")}";
        return procedureCallName;
    }
 
    /**
     * 得到数据类型的整型值
     * 
     * @param typeName
     *            类型名称
     * @return 数据类型的整型值
     */
    private int getDataType(String typeName) {
        if (typeName.equals("varchar"))
            return Types.VARCHAR;
        if (typeName.equals("int"))
            return Types.INTEGER;
        if (typeName.equals("bit"))
            return Types.BIT;
        if (typeName.equals("float"))
            return Types.FLOAT;
        return 0;
    }
 
    // 设置驱动路径
    @SuppressWarnings("static-access")
    public void set_DRIVER(String _DRIVER) {
        this._DRIVER = _DRIVER;
    }
 
    // 设置数据库密码
    @SuppressWarnings("static-access")
    public void set_PASSWORD(String _PASSWORD) {
        this._PASSWORD = _PASSWORD;
    }
 
    // 设置数据库连接字符串
    @SuppressWarnings("static-access")
    public void set_URL(String _URL) {
        this._URL = _URL;
    }
 
    // 设置数据库用户名
    @SuppressWarnings("static-access")
    public void set_USER_NA(String _USER_NA) {
        this._USER_NA = _USER_NA;
    }
 
}

 

posted @ 2013-05-27 16:55 chen11-1 阅读(493) | 评论 (0)编辑 收藏

JS计算包含英文与汉字的字符串长度(一个汉字=2个字节)


function countbCharacters(str)
{
    var totalCount = 0;
    for (var i=0; i<str.length; i++)
    {
        var c = str.charCodeAt(i);
        if ((c >= 0x0001 && c <= 0x007e) || (0xff60<=c && c<=0xff9f))
        {
             totalCount++;
        }
        else
            
             totalCount+=2;
        }
     }
    return totalCount;
}

posted @ 2012-09-17 14:30 chen11-1 阅读(298) | 评论 (0)编辑 收藏

JS 获取字符串长度,截取字符串(中英文,一个汉字相当于2个字符)

<script type="text/javascript">
//一个汉字相当于2个字符
    function get_length(s){
        var char_length = 0;
        for (var i = 0; i < s.length; i++){
            var son_char = s.charAt(i);
            encodeURI(son_char).length > 2 ? char_length += 1 : char_length += 0.5;
        }
        return char_length;
    }
    function cut_str(stbr, len){
        var char_length = 0;
        for (var i = 0; i < str.length; i++){
            var son_str = str.charAt(i);
            encodeURI(son_str).length > 2 ? char_length += 1 : char_length += 0.5;
            if (char_length >= len){
                var sub_len = char_length == len ? i+1 : i;
                return str.substr(0, sub_len);
                break;
            }
        }
    }
//  截取15个字(30个字符)
//  cut_str('aa啊啊啊啊啊啊啊啊啊啊啊啊啊k的啊是', 15);
</script>

posted @ 2012-09-17 14:22 chen11-1 阅读(1895) | 评论 (0)编辑 收藏

JS截取字符长度(按字节)

  1.  
  2. * 
  3.  处理过长的字符串,截取并添加省略号 
  4.  注:半角长度为1,全角长度为2 
  5.   
  6.  pStr:字符串 
  7.  pLen:截取长度 
  8.   
  9.  return: 截取后的字符串 
  10.  *
  11. function autoAddEllipsis(pStr, pLen)  
  12.   
  13.     var _ret cutString(pStr, pLen);  
  14.     var _cutFlag _ret.cutflag;  
  15.     var _cutStringn _ret.cutstring;  
  16.   
  17.     if ("1" == _cutFlag)  
  18.         return _cutStringn "..." 
  19.     else  
  20.         return _cutStringn;  
  21.      
  22.  
  23.   
  24. * 
  25.  取得指定长度的字符串 
  26.  注:半角长度为1,全角长度为2 
  27.   
  28.  pStr:字符串 
  29.  pLen:截取长度 
  30.   
  31.  return: 截取后的字符串 
  32.  *
  33. function cutString(pStr, pLen)  
  34.   
  35.     // 原字符串长度  
  36.     var _strLen pStr.length;  
  37.   
  38.     var _tmpCode;  
  39.   
  40.     var _cutString;  
  41.   
  42.     // 默认情况下,返回的字符串是原字符串的一部分  
  43.     var _cutFlag "1" 
  44.   
  45.     var _lenCount 0;  
  46.   
  47.     var _ret false 
  48.   
  49.     if (_strLen <= pLen/2)  
  50.         _cutString pStr;  
  51.         _ret true 
  52.      
  53.   
  54.     if (!_ret)  
  55.         for (var 0; _strLen i++  
  56.             if (isFull(pStr.charAt(i)))  
  57.                 _lenCount += 2;  
  58.             else  
  59.                 _lenCount += 1;  
  60.              
  61.   
  62.             if (_lenCount pLen)  
  63.                 _cutString pStr.substring(0, i);  
  64.                 _ret true 
  65.                 break 
  66.             else if (_lenCount == pLen)  
  67.                 _cutString pStr.substring(0, 1);  
  68.                 _ret true 
  69.                 break 
  70.              
  71.          
  72.      
  73.       
  74.     if (!_ret)  
  75.         _cutString pStr;  
  76.         _ret true 
  77.      
  78.   
  79.     if (_cutString.length == _strLen)  
  80.         _cutFlag "0" 
  81.      
  82.   
  83.     return {"cutstring":_cutString, "cutflag":_cutFlag};  
  84.  
  85.   
  86. * 
  87.  判断是否为全角 
  88.   
  89.  pChar:长度为1的字符串 
  90.  return: tbtrue:全角 
  91.           false:半角 
  92.  * 

  93.   
  94. function isFull (pChar) 
  95.   for (var 0; < pChar.strLen i++     
  96.     if ((pChar.charCodeAt(i) 128))  
  97.         return true 
  98.     else  
  99.         return false 
  100.     
  101. }
  102. }
  103. 用例:
  104. testStr = "测试1字符串";
    autoAddEllipsis(testStr, 1); // "测..."
    autoAddEllipsis(testStr, 2); // "测..."
    autoAddEllipsis(testStr, 3); // "测..."
    autoAddEllipsis(testStr, 4); // "测试..."
    autoAddEllipsis(testStr, 5); // "测试1..."
    autoAddEllipsis(testStr, 6); // "测试1..."
    autoAddEllipsis(testStr, 7); // "测试1字..."

posted @ 2012-09-17 14:20 chen11-1 阅读(2642) | 评论 (1)编辑 收藏

在线索引重建被取消导致大量ORA-600(kghstack_underflow_internal_3)错误

客户在比较繁忙的时刻执行了索引的REBUILD操作,导致大量会话被锁,最终对ONLINE REBUILD操作执行了取消操作,引发了一系列更严重的ORA-600错误。

 

 

登录数据库后,发现两个节点上ORACLE_BASE所在目录都已经100%占用。数据库无法正常通过/ AS SYSDBA方式登录。

查询告警日志发现大量的ORA-600和ORA-7445错误:

Tue May 08 21:20:26 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_1555.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_3891.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26190.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_2873.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_4518.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:27 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_22469.trc:
ORA-00600: internal error code, arguments: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Tue May 08 21:20:28 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26440.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:29 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_762.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:29 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_26106.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:30 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_1597.trc:
ORA-07445:
出现异常错误:核心转储[0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
Tue May 08 21:20:30 EAT 2012
Errors in file /oracle/app/admin/orcl/udump/orcl2_ora_856.trc:
ORA-07445: exception encountered: core dump [0000000000000000] [SIGSEGV] [Invalid permissions for mapped object] [0x000000000] [] []
.
.
.

检查对应的TRACE文件,发现导致错误语句执行的是TABLE_A表的插入:

ksedmp: internal or fatal error tb
ORA-00600:
内部错误代码,参数: [kghstack_underflow_internal_3], [0x60000000003002F0], [keybuf], [], [], [], [], []
Current SQL statement for this session:
INSERT INTO TABLE_A (O_ID, P_ID, S_ID, F_ITEM, F_NAME, T_MON, D_MON, I_MON, P_STATE, P_TIME, R1, R2) VALUES (:B10 , SUBSTR(:B10 , LENGTH(:B10 ) - 1, 2), :B9 , :B8 , :B7 , :B6 , :B5 , :B4 , :B3 , NULL, :B2 , :B1 )
----- PL/SQL Call Stack -----
 object     line object
 handle   number name
c00000203c2cc550      119 package body U1.P_O_I
c00000203b788200      288 procedure U1.U_B_O_I
c00000203719b8d0        1 anonymous block
----- Call Stack Trace -----
calling             call    entry               argument values in hex     
location            type    point               (? means dubious value)    
-------------------- -------- -------------------- ----------------------------
ksedst()+64         call    ksedst1()           000000000 ? 000000001 ?
ksedmp()+2176       call    ksedst()            000000000 ?
                                                  C000000000000D20 ?
                                                  4000000004037940 ?
                                                  000000000 ? 000000000 ?
                                                  000000000 ?
ksfdmp()+112        call    ksedmp()            000000003 ?
                                                  9FFFFFFFFFFF1230 ?
                                                  60000000000BA290 ?
                                                  9FFFFFFFFFFF1800 ?
                                                  C000000000000999 ?
                                                  400000000407F9B0 ?
kgerinv()+304       call    ksfdmp()            9FFFFFFFFFFF1D90 ?
                                                  000000003 ?
                                                  9FFFFFFFFFFF1810 ?
                                                  60000000000BA290 ?
                                                  C000000000000612 ?
                                                  40000000098C38B0 ?
kgeasnmierr()+144   call    kgerinv()           60000000000318D0 ?
                                                  4000000001AD98A0 ?
                                                  6000000000032988 ?
                                                  4000000001AD98A0 ?
                                                  9FFFFFFFFFFF1DD0 ?
$cold_kghstack_unde call    kgeasnmierr()       60000000000318D0 ?
rflow_internal()+36                               9FFFFFFFBF3B1168 ?
8                                                 9FFFFFFFBF3B1178 ?
                                                  6000000000032D00 ?
                                                  000000002 ?
                                                  60000000003002F0 ?
                                                  000000001 ? 000000006 ?
kghstack_free()+208 call    $cold_kghstack_unde 60000000000318D0 ?
                             rflow_internal()    60000000003002F0 ?
                                                  60000000000BA290 ?
                                                  C000000000000B1D ?
                                                  4000000002F7A510 ?
                                                  00002C87B ?
                                                  6000000000031A10 ?
ksmfrs()+48         call    kghstack_free()     60000000000318D0 ?
                                                  60000000003002F0 ?
rpiswu2()+1312      call    ksmfrs()            60000000003002F0 ?
                                                  C000000000001026 ?
                                                  4000000002F78960 ?
                                                  000000000 ? 000000000 ?
                                                  000000000 ?
rpidrv()+2352       call    rpiswu2()           9FFFFFFFFFFF2AF0 ?
                                                  4000000002F7AE60 ?
                                                  00002F833 ?
                                                  60000000000A7D20 ?
                                                  9FFFFFFFFFFF1E20 ?
                                                  C000000000001ABD ?
                                                  4000000000F14558 ?
                                                  60000000000C6CF0 ?
psddr0()+864        call    rpidrv()            000000018 ? 000000066 ?
                                                  9FFFFFFFFFFF3700 ?
                                                  000000038 ?
                                                  9FFFFFFFFFFF2B20 ?
                                                  60000000000BA290 ?
psdnal()+736        call    psddr0()            000000018 ? 000000066 ?
                                                  9FFFFFFFFFFF3700 ?
                                                  000000030 ?
pevm_EXECC()+832    call    psdnal()            9FFFFFFFFFFF54D0 ?
                                                  C00000203489A9F8 ?
                                                  C0000000000011AA ?
                                                  9FFFFFFFBE832840 ?
                                                  C00000203C2CC550 ?
                                                  400000000313C770 ?
                                                  000026035 ?
pfrinstr_EXECC()+16 call    pevm_EXECC()        9FFFFFFFBE83D1D0 ?
0                                                 9FFFFFFFBE8328B0 ?
                                                  000000020 ?
pfrrun_no_tool()+19 call    pfrinstr_EXECC()    9FFFFFFFBE83D1D0 ?
2                                                 C000001DA198D61C ?
                                                  9FFFFFFFBE83D238 ?
pfrrun()+1376       call    pfrrun_no_tool()    9FFFFFFFBE83D1D0 ?
                                                  000002001 ?
                                                  9FFFFFFFBE83D238 ?
                                                  60000000000BA290 ?
                                                  C000000000000A1C ?
                                                  4000000003163040 ?
                                                  9FFFFFFFBE83D620 ?
                                                  9FFFFFFFBE83D298 ?
plsql_run()+1328    call    pfrrun()            9FFFFFFFFFFF3788 ?
                                                  9FFFFFFFFFFF3770 ?
                                                  60000000000BA290 ?
                                                  9FFFFFFFFFFF4370 ?
                                                  9FFFFFFFFFFF4370 ?
                                                  C000000000000E23 ?
                                                  4000000002C34D70 ?
peicnt()+560        call    plsql_run()         9FFFFFFFFFFF4380 ?
                                                  000000001 ? 000000000 ?
                                                  9FFFFFFFFFFF4380 ?
                                                  60000000000BA290 ?
                                                  9FFFFFFFFFFF4E90 ?
kkxexe()+1008       call    peicnt()            9FFFFFFFFFFF54D0 ?
                                                  9FFFFFFFBE83D1D0 ?
                                                  9FFFFFFFFFFF4EA0 ?
                                                  60000000000BA290 ?
                                                  9FFFFFFFFFFF5420 ?
                                                  C000000000000A1C ?
                                                  000000020 ?
                                                  9FFFFFFFFFFF4F00 ?
opiexe()+8016       call    kkxexe()            0000051F0 ?
.
.
.

这个SQL语句是正常的业务操作,而这种简单的INSERT都会导致ORA-600和ORA-7445错误,说明插入的表或索引本身存在问题。

检查表和索引的状态,发现索引部分分区状态异常:

CREATE INDEX "U1"."IDX_F_2"
ON "U1"."TABLE_A" ("S_ID")
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(
BUFFER_POOL DEFAULT) LOCAL
(PARTITION "P1"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS7" ,
PARTITION "P2"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS8" ,
PARTITION "P3"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS9" ,
PARTITION "P4"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS10" ,
PARTITION "P5"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS1" ,
PARTITION "P6"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS2" ,
PARTITION "P7"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS3" ,
PARTITION "P8"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS4" ,
PARTITION "P9"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS5" ,
PARTITION "P10"
PCTFREE 10 INITRANS 2 MAXTRANS 255 NOLOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TBS6" )

ALTER INDEX "U1"."IDX_F_2" MODIFY PARTITION "P6" UNUSABLE
ALTER INDEX "U1"."IDX_F_2" MODIFY PARTITION "P7" UNUSABLE

和客户沟通后,确认今天问题发生之前有人对索引进行维护操作,操作语句包括:

alter index u1.IDX_F_2 rebuild partition p6 parallel

alter index U1.IDX_T_2 rebuild partition P9 nologging parallel online

alter index U1.IDX_T_1 rebuild partition P3 nologging parallel online

alter index U1.IDX_F_2 rebuild partition P1 nologging parallel online

alter index U1.IDX_T_4 rebuild partition P1 nologging parallel online

alter index U1.IDX_F_4 rebuild partition P5 nologging parallel online

在对索引进行维护之后,数据库中出现了大量的enq: TM – contention等待,TM锁出现的原和是索引IDX_F_2的PARTITION P6重建语句没有添加ONLINE参数有直接关系。随后大量会话被阻塞,而整个索引重建操作被人为中止,其中包括正在运行的ONLINE REBUILD操作,而ONLINE REBUILD操作被中止是十分危险的,很容易导致bug的产生,而当前就导致了ORA-600和ORA-7445的产生。

由于客户着急解决问题,因此对数据库进行了重启。重启后,ORA-600错误信息不再出现。但是前台应用报错,索引失效。

SELECT 'ALTER INDEX U1.' || INDEX_NAME || ' REBUILD PARTITION ' || PARTITION_NAME || ';'
FROM DBA_IND_PARTITIONS
WHERE INDEX_OWNER = 'U1'
AND STATUS != 'USABLE';

获取所有失效的索引重建语句,将索引重新编译后,数据库恢复正常。

在Oracle的MOS上没有找到与之最相关的bug信息,反而是找到了一个相关性很高的补丁信息,而对应的bug信息并没有公开:补丁程序13737888: ONLINE DDL:ORA-600[KGHSTACK_UNDERFLOW_INTERNAL_3], [0X2B7F4E1E7678], [KEYBUF]。

 


posted @ 2012-09-13 17:18 chen11-1| 编辑 收藏

安装ORACLE时在Linux上设置内核参数的含义

前两天看到一篇Redhat官方的Oracle安装文档,对于Linux内核参数的修改描述的非常清晰。

 

 

安装Oracle之前,除了检查操作系统的硬件和软件是否满足安装需要之外,一个重点就是修改内核参数,其中最主要的是和内存相关的参数设置。

SHMMAX参数:Linux进程可以分配的单独共享内存段的最大值。一般设置为内存总大小的一半。这个值的设置应该大于SGA_MAX_TARGET或MEMORY_MAX_TARGET的值,因此对于安装Oracle数据库的系统,shmmax的值应该比内存的二分之一大一些。

# grep MemTotal /proc/meminfo
# cat /proc/sys/kernel/shmmax

上面的命令是检查系统内存的大小,以及当前shmmax的设置。

# echo 21474836480 > /proc/sys/kernetbl/shmmax
# sysctl -w kernel.shmmax=21474836480
# echo "kernel.shmmax=21474836480" >> /etc/sysctl.conf

这是设置shmmax参数的几种方法,这三种方式都可以将shmmax设置为20G。这个参数的修改可以不重启数据库。个人推荐使用第二种sysctl命令的方式。采用第三种方式需要执行sysctl –t操作或重启,但是为了确保下次重启后设置值仍然生效,第三种方式是必不可少的。前两种方式类似alter system set scope = memory,而第三种方式则类似alter system set scope = spfile。

SHMMNI参数:设置系统级最大共享内存段数量。Oracle10g推荐最小值为4096,可以适当比4096增加一些。

# cat /proc/sys/kernel/shmmni
# echo 4096 > /proc/sys/kernel/shmmni
# sysctl -w kernel.shmmni=4096
# echo "kernel.shmmni=4096" >> /etc/sysctl.conf

检查和设置方法如上,这和shmmax的修改方式没有区别,不在赘述。

SHMALL参数:设置共享内存总页数。这个值太小有可能导致数据库启动报错。很多人调整系统内核参数的时候只关注SHMMAX参数,而忽略了SHMALL参数的设置。这个值推荐设置为物理内存大小除以分页大小。

# getconf PAGE_SIZE

通过getconf获取分页的大小,用来计算SHMALL的合理设置值:

SQL> select 32*1024*1024*1024/4096 from dual;

32*1024*1024*1024/4096
----------------------
              8388608

对于32G的内存,4K分页大小的系统而言,SHMALL的值应该设置为8388608。

# cat /proc/sys/kernel/shmall
# echo 8388608 > /proc/sys/kernel/shmall
# sysctl -w kernel.shmall=8388608
# echo " kernel.shmall=8388608" >> /etc/sysctl.conf

查询和设置方法如上。

信号灯semaphores是进程或线程间访问共享内存时提供同步的计数器。

SEMMSL参数:设置每个信号灯组中信号灯最大数量,推荐的最小值是250。对于系统中存在大量并发连接的系统,推荐将这个值设置为PROCESSES初始化参数加10。

SEMMNI参数:设置系统中信号灯组的最大数量。Oracle10g和11g的推荐值为142。

SEMMNS参数:设置系统中信号灯的最大数量。操作系统在分配信号灯时不会超过LEAST(SEMMNS,SEMMSL*SEMMNI)。事实上,如果SEMMNS的值超过了SEMMSL*SEMMNI是非法的,因此推荐SEMMNS的值就设置为SEMMSL*SEMMNI。Oracle推荐SEMMNS的设置不小于32000,假如数据库的PROCESSES参数设置为600,则SEMMNS的设置应为:

SQL> select (600+10)*142 from dual;

(600+10)*142
------------
      86620

SEMOPM参数:设置每次系统调用可以同时执行的最大信号灯操作的数量。由于一个信号灯组最多拥有SEMMSL个信号灯,因此有推荐将SEMOPM设置为SEMMSL的值。Oracle验证的10.2和11.1的SEMOPM的配置为100。

通过下面的命令可以检查信号灯相关配置:

# cat /proc/sys/kernel/sem
250 32000 100 128

对应的4个值从左到右分别为SEMMSL、SEMMNS、SEMOPM和SEMMNI。修改方法为:

# echo 610 86620 100 142 > /proc/sys/kernel/sem
# sysctl -w kernel.sem="610 86620 100 142"
# echo "kernel.sem=610 86620 100 142" >> /etc/sysctl.conf

 

posted @ 2012-09-13 17:17 chen11-1| 编辑 收藏

10G开始Oracle区分物化视图和表

在9i以前,很多功能都是不区分表和物化视图的区别的,到了10g以后。很多功能会将表和物化视图区分对待。

 

 

原本通用的COMMENT ON TABLE语句,对物化视图不再有效,必须要使用COMMENT ON MATERIALIZED VIEW语句代替。

SQL> SELECT * FROM V$VERSION;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

SQL> CREATE TABLE T_BASE (tbID NUMBER, TYPE VARCHAR2(30), NUM NUMBER);

Table created.

SQL> CREATE MATERIALIZED VIEW LOG ON T_BASE
2 WITH ROWID, SEQUENCE (TYPE, NUM)
3 INCLUDING NEW VALUES;

Materialized view log created.

SQL> CREATE MATERIALIZED VIEW MV_BASE
2 REFRESH FAST ENABLE QUERY REWRITE AS
3 SELECT TYPE, SUM(NUM) SUM_NUM, COUNT(NUM) CNT_NUM, COUNT(*) CNT
4 FROM T_BASE
5 GROUP BY TYPE;

Materialized view created.

SQL> COMMENT ON TABLE MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW ';
COMMENT ON TABLE MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW '
*
ERROR at line 1:
ORA-12098: cannot comment on the materialized view


SQL> COMMENT ON MATERIALIZED VIEW MV_BASE IS 'COMMENT ON A MATERIALIZED VIEW ';

Comment created.

SQL> COL COMMENTS FOR A60
SQL> SELECT * FROM USER_MVIEW_COMMENTS;

MVIEW_NAME                    COMMENTS
------------------------------ ------------------------------------------------------------
MV_BASE                       COMMENT ON A MATERIALIZED VIEW

其实不只是COMMENT发生了变化,关于物化视图的执行计划Oracle也对其进行细化,将物化视图的扫描和全表扫描区分开:

SQL> SET AUTOT ON EXP
SQL> SELECT COUNT(*) FROM MV_BASE;

 COUNT(*)
----------
        0

Execution Plan
----------------------------------------------------------
Plan hash value: 3034976462

-------------------------------------------------------------------------
| Id | Operation            | Name   | Rows | Cost (%CPU)| Time    |
-------------------------------------------------------------------------
|  0 | SELECT STATEMENT     |        |    1 |    2  (0)| 00:00:01 |
|  1 | SORT AGGREGATE      |        |    1 |           |         |
|  2 |  MAT_VIEW ACCESS FULL| MV_BASE |    1 |    2  (0)| 00:00:01 |
-------------------------------------------------------------------------

Note
-----
  - dynamic sampling used for this statement

SQL> SELECT /*+ REWRITE */ TYPE, COUNT(*) FROM T_BASE GROUP BY TYPE;

no rows selected

Execution Plan
----------------------------------------------------------
Plan hash value: 1008429399

----------------------------------------------------------------------------------------
| Id | Operation                   | Name   | Rows | Bytes | Cost (%CPU)| Time    |
----------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT            |        |    1 |   30 |    2  (0)| 00:00:01 |
|  1 | MAT_VIEW REWRITE ACCESS FULL| MV_BASE |    1 |   30 |    2  (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Note
-----
  - dynamic sampling used for this statement

在9i以前,很难从执行计划中区分扫描的是表还是物化视图,但是现在一目了然了。

总的来说,这种改进还是很有意义的,用户可以更清楚的了解处理的对象到底是表还是物化视图。

 


posted @ 2012-09-13 17:17 chen11-1| 编辑 收藏

节点重配置后IPC发送超时问题

客户RAC环境在一个节点重启后,另一个节点出现IPC send timeout信息。

 

 

详细错误信息为:

Wed May 2 22:07:00 2012
IPC Send timeout detected.Sender: ospid 20808
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:02 2012
IPC Send timeout detected.Sender: ospid 6677
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:09 2012
IPC Send timeout detected.Sender: ospid 16758
Receiver: inst 1 binc 1718096035 ospid 16261
Wed May 2 22:07:13 2012
IPC Send timeout detected.Sender: ospid 8947
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:13 2012
IPC Send timeout detected.Sender: ospid 6583
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:31 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 132
Wed May 2 22:07:31 2012
IPC Send timeout detected.Sender: ospid 17068
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:34 2012
Communications reconfiguration: tbinstance_number 1
Wed May 2 22:07:34 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 154
Wed May 2 22:07:45 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 64
Wed May 2 22:07:45 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 95
Wed May 2 22:07:54 2012
IPC Send timeout detected.Sender: ospid 21078
Receiver: inst 1 binc 1718095761 ospid 16263
Wed May 2 22:07:59 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 24
Wed May 2 22:08:04 2012
Trace dumping is performing id=[cdmp_20120502220729]
Wed May 2 22:08:24 2012
IPC Send timeout to 0.0 inc 24 for msg type 12 from opid 146
Wed May 2 22:08:36 2012
Trace dumping is performing id=[cdmp_20120502220805]
Wed May 2 22:08:38 2012
Trace dumping is performing id=[cdmp_20120502220805]
Wed May 2 22:10:55 2012
Evicting instance 1 from cluster
Wed May 2 22:11:32 2012
Waiting for instances to leave:
1

这个信息并不正常,查询MOS后发现,这是一个bug,问题描述可以参考:'IPC Send Timeout Detected' errors between QMON Processes after RAC reconfiguration [ID 458912.1]。

对于当前的10.2.0.4环境,需要针对Bug 6200820进行PATCH修正,而对于10.2.0.3版本则需要应用Patch 6326889。

在MOS中查到不少类似IPC Timeout的问题,多数都会影响10.2.0.4版本,且大部分都在10.2.0.5中被fixed,因此如果这个问题出现频繁,升级到10.2.0.5也是一个不错的选择。

posted @ 2012-09-13 17:16 chen11-1| 编辑 收藏

Oracle10g监听listener的总结

 Oracle 10g监听listener不能启动的原因有很多种,本文我们对这些造成listener不能启用的原因进行了总结,接下来就让我们一起来了解一下这部分内容。
   
    1.当遇到Oracle出现下面提示时:ora-01034:oracle not available,ora-27101:shared mermory realm does not exist.
   
    解决方式:
   
    方法1:
   
    1.输入:connect/as sysdba;
   
    2.重启计算机就OK了;
   
    方法2:
   
    在命令行中输入命令如下:
   
    1. C:>svrmgrl
   
    2. Oracle Server Manager Release 3.1.7.0.0 - Production
   
    3. Copyright (c) 2000, Oracle Corporation. tbAll Rights Reserved.
   
    4. Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
   
    5. With the Partitioning option
   
    6. JServer Release 8.1.7.0.0 - Production
   
    7.
   
    8. SVRMGR> connect internal/oracle
   
    9. 连接成功。
   
    10.
   
    11. SVRMGR> startup   startup
   
    12.
   
    后再连接数据库应该没有问题了。
   
    2. Oracle的Listener突然不能启动,错误码是1067.
   
    解决方法:
   
    进入dos
   
    输入lsnrctl start
   
    等待显示结果,我的结果如下
   
    启动tnslsnr:请稍候…
   
    TNSLSNR for 32-bit Windows: Version 9.2.0.1.0 - Production
   
    系统参数文件为E:oracleora92networkadminlistener.ora
   
    写入E:oracleora92networkloglistener.log的日志信息
   
    监听该对象时出错: (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=123.118.1.39)(PORT=1521)))。
   
    TNS-12545:因目标主机或对象不存在,连接失败。
   
    TNS-12560: TNS:协议适配器错误。
   
    TNS-00515:因目标主机或对象不存在,连接失败。
   
    32-bit Windows Error: 49: Unknown error.
   
    监听程序未能启动。请参阅上面的错误消息…
   
    仔细核对,原来是自己修改了Ip地址。
   
    方法1:可修改ip地址。
   
    方法2:可修改E:oracleora92networkadminlistener.ora文件的配置。
   
    3. AGENT服务无法启动
   
    解决方法︰
   
    可以删除networkagent目录下的。q,.ver等文件,然后重新agentctl start即可。
   
    若提示sqlnet.ora错误,可以查看一下sqlnet.ora内容。
   
    1. # SQLNET.ORA Network Configuration file: D:oracleora92networkadminsqlnet.ora
   
    2. # Generated by Oracle configuration tools.
   
    3. SQLNET.AUTHENTICATION_SERVICES= (NTS)
   
    4. NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
   
    三层结构的sqlnet.ora的name
   
    1. # NAMES.PREFERRED_SERVERS =
   
    2. # (ADDRESS_LIST =
   
    3. # (ADDRESS = (PROTOCOL = TCP)(HOST = MICROSOF-3D9384)(PORT = 1575))
   
    4. # )
   
    4. Oracle http server启动不了
   
    把IIS里面用80端口的站点停掉就行了,或者把Oracle Http Server端口改掉。
   
    关于Oracle 10g监听listener不能启动的错误解决方案的总结就介绍到这里了,希望本次的介绍能够对您有所收获!
#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right} #g_kclist a{color:#000; text-decoration:none} #g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff} #g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px} #g_kclist table td{ text-align:center;background:#fff} #g_kclist table td.td1 a{color:#f00} #g_kclist table th{background:#F2F7ED;color:#525F46}

posted @ 2012-09-12 14:52 chen11-1| 编辑 收藏

oracle用户登录审计

 Oracle中可以按照如下方式对用户登陆失败进行审计:

    1、确认sys.aud$ 是否存在?

    desc sys.aud$

    2、观察user$表中lcount为非0的用户,如果包含被锁账户,则可以判定很有可能是该用户登陆尝试失败过多

    造成了账户被锁:

    select name,lcount from sys.user$;

    3、修改audit参数: audit_trail=none

    alter system set audit_trail=db scope=spfile;

    重启数据库。参数生效。

    4、开启tb登陆失败审计:

    AUDIT SESSION WHENEVER NOT SUCCESSFUL;

    5、登陆失败尝试。

    sqlplus w/错误密码

    6、检查审计记录

    select * from sys.aud$;

    里面有会话基本信息和机器名,用户名等。

    解锁用户

    alter user atest account unlock;

    解除由于密码连续错误而锁定用户

    alter profile default limit failed_login_attempts unlimited;

#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right} #g_kclist a{color:#000; text-decoration:none} #g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff} #g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px} #g_kclist table td{ text-align:center;background:#fff} #g_kclist table td.td1 a{color:#f00} #g_kclist table th{background:#F2F7ED;color:#525F46}

posted @ 2012-09-12 14:51 chen11-1| 编辑 收藏

异构服务提高Oracle连接异种数据源能力

由于历史的原因,在多数企业都同时存在多个数据库平台,在每个数据库平台上都运行着相关的一套或多套应用。随着单位业务不断扩大,如何在不影响现有应用运行的前提下,快速有效地整合这些分布在单位内部不同数据库平台上的数据,是一个困扰CIO们的问题。面对这一问题,现有解决方案大致可分为以下两种:

  1.在应用程序上建立连接不同数据源的数据连接,这样做要求程序员分清哪个连接是对应哪个数据库的,而且如果设计时涉及到存储过程还要按照不同数据库的要求分别编写,加重了程序员的要求。

  2.在数据库中设立快照,定时把其他数据源的数据复制到本地数据库,这样虽然解决了前一种方法中不同数据源的问题,但是由于是定时复制,数据不能实时同步,在实时性要求高的应用中这种方法便不能使用。

  由于上述两种方法都存在一定的缺点,这里介绍一种Oracle提供的解决oracle数据库与异种数据源的连接问题的解决方案—Oracle的异构服务(Heterogeneous Services)。

  程序运行效果截图

  异构服务

  “异构服务”是集成在Oracle 8i数据库软件中的功能,它提供了从Oracle数据库访问其他非Oracle数据库的通用技术。熟悉Oracle的读者都很清楚,Oracle提供通过建立DB Link的方法访问非本地数据库,而“异构服务”提供通过建立DB Link使你能够执行Oracle SQL查询,透明地访问其他非Oracle数据库里的数据,就像访问Oracle远程数据库一样。“异构服务”分为两种:

  1. 事务处理服务(Transation Service):通过事务处理服务,使用户在访问非Oracle数据库中支持事务处理功能。

  2. SQL服务: 通过SQL服务,使用户直接在Oracle数据库中执行对非Oracle数据库的各种SQL语句。

  根据异构服务代理程序的不同,“异构服务”连接方式可以分为透明网关和通用连接两种。

  透明网关(Transparent Gateways) 透明网关使用Oracle提供的特定网关程序来设置代理,tb例如连接SQL Server则必须要有SQL Transparent Gateway for SQL Server。

  通用连接(Generic Connectivity) 通用连接又分为ODBC连接和OLE DB连接两种,其连接方法和透明网关没有本质区别,只不过通用连接是和数据库一起提供的功能,你不需要向Oracle购买相关的透明网关程序。

  连接实例

  这个实例的应用环境是Oracle 8.1.7,操作系统Windows 2000 Server英文版,采用通用连接的ODBC for SQL Server连接SQL Server 2000中文版。安装步骤如下:

  1. 安装HS部件。

  缺省情况下,HS服务是和Oracle 8.1.7一起安装的,你可以查询SYS用户下是否存在HS_BASE_CAPS视图,以确认HS部件是否安装,如果没有可以用相关的安装盘进行安装。

  2. 配置ODBC系统连接字。

  在控制面板选择“Data Sources (ODBC)”,在“系统DNS”内配置ODBC for SQL Server连接字(dnsora2sql)。

  3.配置tnsnames.ora,它位于ORACLE_HOME\NETWORK\ADMIN。

  在这个文件中增加如下代码:

  Lnk2sql =

  (DESCRIPTION =

  (ADDRESS_LIST =

  (ADDRESS = (PROTOCOL = TCP)(HOST = HOSTNAME)(PORT = 1521)))

  (CONNECT_DATA =

  (SID = hs4sql) |< 服务的SID名称,要和Listener里配置的sid相同)

  (HS=OK) |< 打开HS服务选项

  )

  4.配置listener.ora,它位于ORACLE_HOME\NETWORK\ADMIN。

  在这个文件中增加如下代码:

  SID_LIST_LISTENER =

  (SID_LIST =

  (SID_DESC =

  (SID_NAME = hs4sql) |< 服务的SID名称,与tnsname名称相对应

  (ORACLE_HOME = C:\oracle\ora9201)

  (PROGRAM = hsodbc) |< 要使用的HS服务程序,如果使用OLE DB,程序名为hsole )

  )

  5. 重新启动Oracle listener。

  6. 编辑位于ORACLE_HOME\HS\ADMIN内init.ora,这里是iniths4sql。

  修改如下两行代码:

  HS_FDS_CONNECT_INFO = dnsora2sql |< ODBC系统名

  HS_FDS_TRACE_LEVEL = 0

  7. 创建DATABASE LINK。

  create database link ‘ora2sql’ connect

  to sql1 identified by sql1 using ‘lnk2sql’;

  8. 测试连接。如:

  SQL> select * from   到此我们已经完成了使用ODBC连接SQL SERVER的配置工作。

  总体上说,异构服务扩展了Oracle数据库连接异种数据源的能力,加强了企业数据的整合,是一个快速有效经济地整合企业内部异构数据的解决方案。

posted @ 2012-09-12 14:50 chen11-1| 编辑 收藏

Oracle中利用ADO对象实现存取和访问

 ADO是Active Data Object的缩写,称为ActiveX数据对象。利用ADO对象,通过ODBC驱动程序或OLE DB连接字符串,可实现对任意数据库的存取和访问。

  OLE DB是微软用来替代ODBC的一种数据库访问技术。是一种对关系型数据库和非关系型数据库均有效的一种数据库访问技术。

  ADO提供了7个独立的对象,利用这些对象,可实现对数据库的存取和访问:

  ·Connection 连接对象。

  ·Command 命令对象,利用命令对象可执行一个SQL存储过程或有参数的查询。

  ·Parameter 参数对象。

  ·Recordset 记录集对象,代表从数据表中通过查询所获得的一组记录。通过该对象的方法和属性,可实现对记录的存取和操作。

  ·Field 字段对象,代表记录集中的一个字段。Fields为字段集合,tb代表一条记录的全部字段。

  ·Property 属性对象。

  ·Error 错误对象。

  Connection,Command和Recordset是整个ADO的核心,通过Connection对象与一个数据库建立连接,然后利用Command对象执行查询,从而返回查询结果,并将结果(记录集)存入Recordset对象中,利用服务器端脚本,通过访问Recordset对象,便可获得查询到的记录内容。

  另外,利用Connection对象的Execute方法和Recordset对象的Open方法,也可执行一个查询,返回一个记录集。

  ODBC数据源的用户数据源,系统数据源和文件数据源。用户和系统DSN存储在WindowsNT注册表中,系统DSN可被登录的所有用户访问和使用,用户DSN只能提供特定的用户访问和使用。 文件DSN是存储在一个扩展名为.dsn的文本文件中,可供多个用户访问和使用,并可实现复制,通用性强,一般采用此方式。

  ADO连接对象

  连接对象在使用前必须先创建该对象的实例:

  Set 实例名=Server.CreateObject("ADODB.Connection")

  连接对象的方法:

  ·Open方法

  连接对象.Open 数据源名 | 连接字符串

  带参数调用Open方法时,其参数实质是传递给连接对象的ConnectionString属性的。因此,可事先设置ConnectionString属性的值,然后再调用不带参数的Open方法。

  ·Close方法

  连接对象.Close 释放:Set conn=Nothing

  ·Execute方法

  该方法用于执行SQL语句。根据SQL语句执行后是否返回记录集,该方法的使用格式分为以下两种:

  1.执行SQL查询语句时,将返回查询得到的记录集。

  用法为:

  Set 对象变量名=连接对象.Execute("SQL 查询语言")

  Execute方法调用后,会自动创建记录集对象,并将查询结果存储在该记录对象中,通过Set方法,将记录集赋给指定的对象保存,以后对象变量就代表了该记录集对象。

  2.执行SQL的操作性语言时,没有记录集的返回。

  此时用法为:

  连接对象.Execute "SQL 操作性语句" [, RecordAffected][, Option]

  ·RecordAffected 为可选项,此出可放置一个变量,SQL语句执行后,所生效的记录数会自动保存到该变量中。通过访问该变量,就可知道SQL语句队多少条记录进行了操作。

  ·Option 可选项,该参数的取值通常为adCMDText,它用于告诉ADO,应该将Execute方法之后的第一个字符解释为命令文本。通过指定该参数,可使执行更高效。

  ·BeginTrans、RollbackTrans、CommitTrans方法

  这三个方法是连接对象提供的用于事务处理的方法。BeginTrans用于开始一个事物;RollbackTrans用于回滚事务;CommitTrans用于提交所有的事务处理结果,即确认事务的处理。

  事务处理可以将一组操作视为一个整体,只有全部语句都成功执行后,事务处理才算成功;若其中有一个语句执行失败,则整个处理就算失败,并恢复到处里前的状态。

  BeginTrans和CommitTrans用于标记事务的开始和结束,在这两个之间的语句,就是作为事务处理的语句。判断事务处理是否成功,可通过连接对象的Error集合来实现,若Error集合的成员个数不为0,则说明有错误发生,事务处理失败。Error集合中的每一个Error对象,代表一个错误信息。

  另外,利用SQL本身所提供的事务处理语句,通过编写存储过程,然后利用ADO命令对象的相关方法,通过调用执行存储过程,也可实现事务。

posted @ 2012-09-12 14:49 chen11-1| 编辑 收藏

oracle认证辅导:DECODE()函数用法

DECODE()函数用法
   
    作用:将输入数值与函数中的参数列表相比较,根据输入值返回一个对应值。函数的参数列表是由若干数值及其对应结果值组成的若干序偶形式。当然,假如未能与任何一个实参序偶匹配成功,则函数也有默认的返回值。 区别于SQL的其它函数,DECODE函数还能识别和操作空值。
   
    语法:DECODE(control_value,value1,result1[,value2,result2…][,default_result]);
   
    control _value试图处理的数值。DECODE函数将该数值与后面的一系列的偶序相比较,以决定返回值。 value1是一组成序偶的数值。假如输入数值与之匹配成功,则相应的结果将被返回。对应一个空的返回值,可以使用要害字NULL于之对应 result1 是一组成序偶的结果值。 default_result 未能与任何一个值匹配时,函数返回的默认值。
   
    例如: selectdecode( x , 1 , 'x is 1 ', 2 , 'x is 2 ', 'others') from dual 当x等于1时,则返回'x is 1'. 当x等于2时,则返回'x is 2'. 否则,返回others'. 需要,比较2个值的时候,可以配合SIGN()函数一起使用。 SELECT DECODE( SIGN(5 -6), 1 'Is Positive', -1, 'Is Nagative', 'Is Zero') 同样,也可以用CASE实现: SELECT CASE SIGN(5 - 6) WHEN 1 THEN 'Is Positive'WHEN -1 THEN 'Is Nagative'ELSE 'Is Zero' ENDFROM DUAL此外,还可以在Order by中使用Decode. 例如:表table_subject,有subject_name列。要求按照:语、数、外的顺序进行排序。tb这时,就可以非常轻松的使用Decode完成要求了。 select * from table_subject order by decode(subject_name, '语文', 1, '数学', 2, , '外语',3)
   
    将所有的结果全部写出
   
    select * from classes t
   
    数据为
   
    1 1 一班 NUM_1
   
    2 2 二班 NUM_2
   
    3 3 三班 NUM_3
   
    4 4 四班 NUM_4
   
    select t.* from classes t order by decode(t.classnum,'NUM_1',4,'NUM_2',3,'NUM_3',1);
   
    1 3 三班 NUM_3
   
    2 2 二班 NUM_2
   
    3 1 一班 NUM_1
   
    4 4 四班 NUM_4
   
    必须将所有的结果写出

posted @ 2012-09-11 14:57 chen11-1| 编辑 收藏

Oracle承诺整合Fusion的安全特性

 本周Oracle宣布下一步将全力实施它的安全策略,并透露计划去整合安全产品到应用产品线同时扩展企业安全搜索。为确保自己成为一家名副其实的安全产品厂商,Oracle花费了大量资金,先后收购了许多访问管理、身份管理和其他安全性软件制造商。

  在周三的安全性策略简报中,Oracle公司承诺将会整合些安全性产品到自己的Fusion应用系统中。该系统将在2008年发布。公司还计划下个月扩展新的企业安全搜索的能力,允许搜索引擎不仅可以搜索Oracle应用系统和数据库而且还可直接访问电子邮件和文件系统。

  当Oracle做到产品安全时,它的范围将是非常广阔的。在数据库方面,公司提出高级安全性选项用于数据加密,数据库保护用于防止内部用户滥用他们的数据访问权限,安全标签用于指定不同安全级别的数据,安全备份用于加密备份的磁带。Oracle还提供了多样的身份管理能力,包括目录服务、身份验证、web服务访问控制。Oracle还将审计和一致性应用作为其安全措施的一部分。

  如何辨别安全领域的产品主要看产品是否具备自动安全处理能力如身份管理和数据保护。Oracle负责服务器技术的高级副总裁Thomas Kurian说:“通过Fusion中我们将整合那些全部的安全特性到我们的企业应用中。”但这并意味着Oracle的单机安全应用会在近期发布,目前它还在研发过程中。所以客户需要继续去管理他们的非Oracle的应用系统。

  Oracle的企业安全搜索可以让企业员工在整个公司数据库和应用系统中搜寻信息。Oracle总裁Charles Phillips说:“客户说希望让他们的用户能够象用Google一样的搜索,但不能搜寻到他们不该看到的信息。”企业安全搜索设计与Oracle Portal开发的企业门户配合使用,如同在Oracle数据库开发数据库应用。公司计划去新增个适配器实现企业对微软Exchange和SharePoint的搜索。

  企业策略组织的高级分析师Jon Oltsik说,Oracle认定产品的安全特性有广阔市场机会,同样市场目标IBM收购了Internet Security Systems,EMC收购了RSA Security。但是客户关心的是增强他们IT基础设置的安全性,而不是去防止他们咨询奇特的问题。虽然历史不是总能暗示出未来,但IT安全支持者不可能忘记在Oracle危急补丁升级期间的那些坏经历。当Oracle转向安全领域时,它被人们认为是卖万金油的,主要就是他们产品质量的因素。

  投资者金融服务公司Investors Bank & Trust早已经使用了Oracle的电子商务应用和数据库,但是系统使用的效果没有之前预期的好。该金融服务公司在过去两年一直是采用的IBM的访问和身份管理技术。Investors Bank & Trust公司企业应用主管说:"如果Oracle真的能够将安全特性整合到他们的产品中,能够他们承诺的那样去做的话,他们的软件产品将变的更加引人注目"。

  从目前来看,Oracle Fusion计划的实现还有很长的路要走。在这期间Oracle需要继续面对那些长期的竞争对手,tb如CA,IBM和sun等。现在的疑问不是Oracle能不能够将安全性产品推销给自己的客户群而是他能不能就产品销售给竞争对手的客户。

#g_kclist{font-size:12px;width:570px;float:none; margin-top:5px; clear:right} #g_kclist a{color:#000; text-decoration:none} #g_kclist h2{margin:0px;padding:0px;font-size:14px; text-align:center;background:url(http://www.thea.cn/zt/zt_img/zczhongduan.gif) no-repeat;line-height:31px;color:#fff} #g_kclist table{line-height:25px;background:#B0DA90;margin-top:8px} #g_kclist table td{ text-align:center;background:#fff} #g_kclist table td.td1 a{color:#f00} #g_kclist table th{background:#F2F7ED;color:#525F46}

posted @ 2012-09-11 14:57 chen11-1| 编辑 收藏

Oracle回滚段空间回收步骤

是谁"偷偷的"用了那么多空间呢(本来有几十个G的Free磁盘空间的)?

  检查数据库表空间占用空间情况:

  SQL> select tablespace_name,sum(bytes)/1024/1024/1024 GB

  2 from dba_data_files group by tablespace_name

  3 union all

  4 select tablespace_name,sum(bytes)/1024/1024/1024 GB

  5 from dba_temp_files group by tablespace_name order by GB;

  TABLESPACE_NAME                        GB

  ------------------------------ ----------

  USERS                          .004882813

  UNDOTBS2                        .09765625

  SYSTEM                         .478515625

  SYSAUX                         .634765625

  WAPCM_TS_VISIT_DETAIL            .9765625

  HY_DS_DEFAULT                           1

  MINT_TS_DEFAULT                         1

  MMS_TS_DATA2                        1.375

  MMS_IDX_SJH                             2

  MMS_TS_DEFAULT                          2

  IVRCN_TS_DATA                           2

  TABLESPACE_NAME                        GB

  ------------------------------ ----------

  MMS_TS_DATA1                            2

  CM_TS_DEFAULT                           5

  TEMP                           20.5498047

  UNDOTBS1                       27.1582031

  15 rows selected.

  不幸的发现,UNDO表空间已经扩展至27G,而TEMP表空间也扩展至20G,这2个表空间加起来占用了47G的磁盘空间,导致了空间不足。

  显然曾经有大事务占用了大量的UNDO表空间和Temp表空间,Oracle的AUM(Auto Undo Management)从出生以来就经常出现只扩展,不收缩(shrink)的情况(通常我们可以设置足够的UNDO表空间大小,然后取消其自动扩展属性).

  现在我们可以采用如下步骤回收UNDO空间:

  1.确认文件

  SQL> select file_name,bytes/1024/1024 from dba_data_files

  2 where tablespace_name like 'UNDOTBS1';

  FILE_NAME

  --------------------------------------------------------------------------------

  BYTES/1024/1024

  ---------------

  +ORADG/danaly/datafile/undotbs1.265.600173875

  27810

  2.检查UNDO Segment状态

  SQL> select usn,xacts,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks

  2 from v$rollstat order by rssize;

  USN      XACTS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024    SHRINKS

  ---------- ---------- --------------------- ---------------------- ----------

  0          0            .000358582             .000358582          0

  2          0            .071517944             .071517944          0

  3          0             .13722229              .13722229          0

  9          0            .236984253             .236984253          0

  10          0            .625144958             .625144958          0

  5          1            1.22946167             1.22946167          0

  8          0            1.27175903             1.27175903          0

  4          1            1.27895355             1.27895355          0

  7          0            1.56770325             1.56770325          0

  1          0            2.02474976             2.02474976          0

  6          0             2.9671936              2.9671936          0

  11 rows selected.

  3.创建新的UNDO表空间

  SQL> create undo tablespace undotbs2;

  Tablespace created.

  4.切换UNDO表空间为新的UNDO表空间

  SQL> alter system set undo_tablespace=undotbs2 scope=both;

  System altered.

  此处使用spfile需要注意,以前曾经记录过这样一个案例:Oracle诊断案例-Spfiletb案例一则

  5.等待原UNDO表空间所有UNDO SEGMENT OFFLINE

  SQL> select usn,xacts,status,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks

  2 from v$rollstat order by rssize;

  USN      XACTS STATUS          RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024    SHRINKS

  ---------- ---------- --------------- --------------------- ---------------------- ----------

  14          0 ONLINE                     .000114441             .000114441          0

  19          0 ONLINE                     .000114441             .000114441          0

  11          0 ONLINE                     .000114441             .000114441          0

  12          0 ONLINE                     .000114441             .000114441          0

  13          0 ONLINE                     .000114441             .000114441          0

  20          0 ONLINE                     .000114441             .000114441          0

  15          1 ONLINE                     .000114441             .000114441          0

  16          0 ONLINE                     .000114441             .000114441          0

  17          0 ONLINE                     .000114441             .000114441          0

  18          0 ONLINE                     .000114441             .000114441          0

  0          0 ONLINE                     .000358582             .000358582          0

  USN      XACTS STATUS          RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024    SHRINKS

  ---------- ---------- --------------- --------------------- ---------------------- ----------

  6          0 PENDING OFFLINE             2.9671936              2.9671936          0

  12 rows selected.

  再看:

  11:32:11 SQL> /

  USN      XACTS STATUS          RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024    SHRINKS

  ---------- ---------- --------------- --------------------- ---------------------- ----------

  15          1 ONLINE                     .000114441             .000114441          0

  11          0 ONLINE                     .000114441             .000114441          0

  12          0 ONLINE                     .000114441             .000114441          0

  13          0 ONLINE                     .000114441             .000114441          0

  14          0 ONLINE                     .000114441             .000114441          0

  20          0 ONLINE                     .000114441             .000114441          0

  16          0 ONLINE                     .000114441             .000114441          0

  17          0 ONLINE                     .000114441             .000114441          0

  18          0 ONLINE                     .000114441             .000114441          0

  19          0 ONLINE                     .000114441             .000114441          0

  0          0 ONLINE                     .000358582             .000358582          0

  11 rows selected.

  Elapsed: 00:00:00.00

  6.删除原UNDO表空间

  11:34:00 SQL> drop tablespace undotbs1 including contents;

  Tablespace dropped.

  Elapsed: 00:00:03.13

  7.检查空间情况

  由于我使用的ASM管理,可以使用10gR2提供的信工具asmcmd来察看tb空间占用情况.

  [oracle@danaly ~]$ export ORACLE_SID=+ASM

  [oracle@danaly ~]$ asmcmd

  ASMCMD> du

  Used_MB      Mirror_used_MB

  21625               21625

  ASMCMD> exit

  空间已经释放。

.item-area{width:578px;margin:15px auto;border-top:1px solid #ddd;color:#666} .item-area a,.item-area a:link,.item-area a:visited{color:#666;text-decoration:none} .item-area a:hover{color:#3a7ad9;text-decoration:underline;} a img{border:none;vertical-align:middle} .item-area h2,.item-area h3{float:none;font-size:100%;font-weight:normal;} .item-area .h2{height:25px;margin:10px 0;padding-left:35px;*float:left;font:bold 14px/25px "宋体";background:url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 0} .item-area span.more{float:right;font:normal 12px/25px "宋体"} .item-area a.more{float:right;font:normal 12px/25px "宋体"} .item-a{margin-bottom:15px} .item-a .h-ksrm{background-position:0 0} .item-a li{*display:inline;overflow:hidden;zoom:1;line-height:2em;padding-left:35px;font-size:14px;background: url(http://sns.thea.cn/module/images/btns.png) no-repeat -1px -28px;} .item-a li a{float:left;} .item-a .testBtn{float:right;width:58px;height:21px;line-height:21px;font-size:12px;margin-top:5px;margin-top:3px;text-align:center;background:url(http://sns.thea.cn/module/images/btns.png) no-repeat -1px -1px; color:#FFFFFF;} .item-a a.freeBtn{width:20px;margin:0 0 0 6px;line-height:28px;color:#fff;font-size:12px;text-indent:-9999px;background: url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 -131px;} .item-a li.hots a.freeBtn{background-position:0 -105px} .item-a a.examnum em{font-style:normal;color:red;font-weight:bold;} .item-b {padding:5px 0 20px;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd} .xsjl-list-col3s li{display:table-cell;*display:inline;zoom:1;vertical-align:top;width:182px;padding-right:10px;line-height:150%;font-size:12px;} .item-b .h-xsjl{background-position:0 -26px} .item-b .pic{float:left;margin:3px 10px 0 0;} .item-b em{font-style:normal;color:#dc2c2c} .item-b a.join{display:inline-block;padding-left:20px;background:url(http://sns.thea.cn/module/images/icos.png) no-repeat 0 -160px} .item-b .xsjl-list-col3s h3 a{display:inline-block;width:120px;overflow:hidden;white-space:nowrap;color:#3a7ad9} .item-b .xsjl-list-col3s h3{text-align:left;line-height:150%;font-family:"宋体","微软雅黑"}

posted @ 2012-09-11 14:55 chen11-1| 编辑 收藏

sqlplus直连数据库出现ORA-27504错误

客户数据库使用sqlplus直连方式连接数据库报错,而如果使用tnsnames方式则可以正常连接。

 

 

详细错误信息为:

Thu Apr 26 10:17:56 2012
Errors in file /oracle/admin/trs/udump/trs2_ora_2619.trc:
ORA-00603: ORACLE server session terminated by tb fatal error
ORA-27504: IPC error creating OSD context
ORA-27300: OS system dependent operation:IPC init failed with status: 65
ORA-27301: OS failure message: Package not installed
ORA-27302: failure occurred at: skgxpcini
ORA-27303: additional information: libskgxpd.so called
libskgxp10.so should reference real implementation.

根据MOS文档,这个问题的原因是由于环境变量中指定了CRS的目录,导致部分LIB没有找到:sqlplus Local connection to Instance is not possible , remote Using tns is fine . [ID 859778.1]。

解决问题的方法是在环境变量SHLIB_PATH和LIBPATH中,去掉CRS的HOME信息,使得Oracle正确找到ORACLE_HOME下的LIB目录。

posted @ 2012-09-10 14:31 chen11-1| 编辑 收藏

RAC节点频繁重启出现ORA-29702

数据库的Oracle 10204 RAC for Windows出现频繁节点重启的问题。

 

 

从告警日志看,当前节点的重启一般发生在节点刚启动或关闭时:

Thu May 03 17:22:45 2012
cluster interconnect IPC tb version:Oracle 9i Winsock2 TCP/IP IPC
IPC Vendor 0 proto 0
Version 0.0
PMON started with pid=2, OS id=1616
DIAG started with pid=3, OS id=120
PSP0 started with pid=4, OS id=6104
LMON started with pid=5, OS id=3844
LMD0 started with pid=6, OS id=6120
LMS0 started with pid=7, OS id=3548
LMS1 started with pid=8, OS id=5688
LMS2 started with pid=9, OS id=3636
LMS3 started with pid=10, OS id=3588
MMAN started with pid=11, OS id=3168
DBW0 started with pid=12, OS id=3208
DBW1 started with pid=13, OS id=5784
LGWR started with pid=14, OS id=6208
CKPT started with pid=15, OS id=3100
SMON started with pid=16, OS id=5948
RECO started with pid=17, OS id=3748
CJQ0 started with pid=18, OS id=7152
MMON started with pid=19, OS id=4552
MMNL started with pid=20, OS id=6940
Thu May 03 17:22:46 2012
lmon registered with NM - instance id 1 (internal mem no 0)
Thu May 03 17:22:46 2012
Reconfiguration started (old inc 0, new inc 8)
List of nodes:
0 1
Global Resource Directory frozen
* allocate domain 0, invalid = TRUE
Communication channels reestablished
Error: KGXGN aborts the instance (6)
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lmon_3844.trc:
ORA-29702: ???????????

LMON: terminating instance due to error 29702
Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_pmon_1616.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_psp0_6104.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_dbw0_3208.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_mman_3168.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_dbw1_5784.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_ckpt_3100.trc:
ORA-29702: ???????????

Thu May 03 17:22:51 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lgwr_6208.trc:
ORA-29702: ???????????

Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_reco_3748.trc:
ORA-29702: ???????????

Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_smon_5948.trc:
ORA-29702: ???????????

Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lms1_5688.trc:
ORA-29702: ???????????

Thu May 03 17:22:52 2012
Errors in file d:\oracle\product\10.2.0\admin\orcl\bdump\orcl1_lms0_3548.trc:
ORA-29702: ???????????

Instance terminated by LMON, pid = 3844

而从CSSD日志文件中可以发现下面的信息:

[ CSSD]2012-04-29 16:26:07.953 [7112] >TRACE: clssgmReconfigThread: completed for reconfig(13), with status(1)
2012-04-30 09:07:04.718: [ OCROSD]utgdv:11:could not read reg value ocrmirrorconfig_loc os error=
操作系统找不到已输入的环境选项。

2012-04-30 09:07:04.718: [ OCROSD]utgdv:11:could not read reg value ocrmirrorconfig_loc os error=操作系统找不到已输入的环境选项。

[ CSSD]2012-04-30 09:07:04.765 >USER: Copyright 2012, Oracle version 10.2.0.4.0
[ CSSD]2012-04-30 09:07:04.765 >USER: CSS daemon log for node crct-oadb, number 1, in cluster crs
[ CSSD]2012-04-30 09:07:04.765 [3780] >TRACE: clssscmain: local-only set to false
[ clsdmt]Listening to (ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=61180))
[ CSSD]2012-04-30 09:07:04.781 [3780] >TRACE: clssnmReadNodeInfo: added node 1 (crct-oadb) to cluster
[ CSSD]2012-04-30 09:07:04.781 [3780] >TRACE: clssnmReadNodeInfo: added node 2 (crct-oapt) to cluster
[ CSSD]2012-04-30 09:07:04.828 [3724] >TRACE: clssnm_skgxninit: Compatible vendor clusterware not in use
[ CSSD]2012-04-30 09:07:04.828 [3724] >TRACE: clssnm_skgxnmon: skgxn init failed
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmNMInitialize: misscount set to (60)
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmNMInitialize: Network heartbeat thresholds are: impending reconfig 30000 ms, reconfig start (misscount) 60000 ms
[ CSSD]2012-04-30 09:07:04.843 [3780] >TRACE: clssnmDiskStateChange: state from 1 to 2 disk (0/\\.\votedsk1)
[ CSSD]2012-04-30 09:07:04.843 [3112] >TRACE: clssnmvDPT: spawned for disk 0 (\\.\votedsk1)
[ CSSD]2012-04-30 09:07:06.843 [3112] >TRACE: clssnmDiskStateChange: state from 2 to 4 disk (0/\\.\votedsk1)
[ CSSD]2012-04-30 09:07:06.843 [4492] >TRACE: clssnmvKillBlockThread: spawned for disk 0 (\\.\votedsk1) initial sleep interval (1000)ms

根据这些信息查询,发现属于10.2.0.4上的bug:10gR2/11gR1: Instances Abort With ORA-29702 When The Server is rebooted or shut down [ID 752399.1]。这个bug影响10.2.0.1到10.2.0.4以及11.1.0.6和11.1.0.7版本。

Oracle给出的解决方案是修改操作系统启动时调用的K96 link替换为K19 link。不过当前版本是Windows环境,显然这种解决方法并不适用。恐怕除了升级版本外,没有什么太好的其他解决方法。

将产品环境部署在Windows环境下的系统确实少见,而在Windows上部署RAC的就更是凤毛麟角了,而大多数这样部署的不只是对于Oracle不了解,连Windows和Linux的稳定性的差别都不是很清楚,出现各种问题的几率自然要大得多了。

 


posted @ 2012-09-10 14:30 chen11-1| 编辑 收藏

系统空间不足产生ORA-1错误

ORA-1错误代表唯一冲突,而空间不足时出现这个错误还是第一次碰到。

 

 

错误信息如下:

Tue May 01 05:00:11 2012
Non critical error ORA-00001 caught while writing to trace file "/home/oracle/base/diag/rdbms/orcl/orcl/trace/orcl_ora_31131.trc"
Error message: Linux-x86_64 Error: 28: No space left on device
Additional information: 1
Writing to the above trace file is disabled for now on...

这个错误是11.2.0.2引入的,Oracle本应该返回操作系统上的错误,结果返回了ORA-00001错误信息,这是一个未发布的BUG 8367518导致的,可以参考文档Non Critical Error ORA-00001 Caught While Writing To Trace File tb[ID 1354863.1]。

Oracle给出的解决方法就是应用BUG 8367518的补丁,当然这个问题并不严重,完全可以将其忽略。

posted @ 2012-09-10 14:28 chen11-1| 编辑 收藏

GoldenGate导致的Streams miscellaneous event等待事件

客户一个并不繁忙的数据库出现长时间Streams miscellaneous event等待。

 

 

数据库版本是RAC 11.2.0.2 for Linux X64,其中一个节点的TOP 5等待信息为:

Event

Waits

Time(s)

Avg wait (ms)

% DB time

Wait Class

Streams miscellaneous event

62,377

31,274

501

93.26

Other

DB CPU

 

1,887

 

5.63

 

log file sequential read

648,206

276

0

0.82

System I/O

control file sequential read

519,487

122

0

0.36

System I/O

Disk file operations I/O

483,960

99

0

0.30

User I/O

显然,这个数据库的主要负载发生在这个等待事件上,而这个系统中部署了Goldengate。查询MOS,很容易找到文档Why do I see Streams Miscellaneous Event in AWR as a Top Event When GoldenGate Extractb is Running [ID 1317122.1]。

文档对这个问题进行了简单的描述,这个等待事件是Goldengate在等待日志中的额外工作的,在11.2.0.2.X版本以后,这个等待事件改名为Waiting for additional work from the logfile,而且被记入到空闲等待中。

对于这个问题,可以安全的将其忽略掉。

 


posted @ 2012-09-10 14:27 chen11-1 阅读(1209) | 评论 (0)编辑 收藏

关于RTX二次开发解决方案

关于RTX二次开发
请问各位高手,怎么能够实现RTX与OA的反向登录啊?由RTX登录OA(有具体代码最好了,RTXClientSDKHandbook.CHM文档 我有)

------解决方案--------------------------------------------------------
自己先顶!!!
------解决方案--------------------------------------------------------
由OA登录RTX很简单,但是反过来,比较难!
------解决方案--------------------------------------------------------
没有搞过rtx啊。原理应该差不多吧,传用户名和密码到OA的登陆页,然后提交就可以了。
------解决方案--------------------------------------------------------
来顶贴!
------解决方案--------------------------------------------------------
就是SSO啊,TB模拟登录

posted @ 2012-09-06 19:58 chen11-1 阅读(323) | 评论 (0)编辑 收藏

为啥条码打印出来,扫描枪不能扫描

为什么条码打印出来,扫描枪不能扫描?
条码字体我是从http://font.chinaz.com/TiaoXingMaZiTi.html上下载下来的,字体名字叫做IntHrP72DlTt.TTF,我把字体的fontsize设成了24,但是为什么我打印出来的条码,扫描枪却无法识别呢,扫描枪时二维扫描枪ms1690,测试过无问题

------解决方案--------------------------------------------------------
to use the code providey by the printer provider
------解决方案--------------------------------------------------------
条码打印有没有超出条码纸的边界,如果没有超过,tb把字体调大一下,如果超过把字体调小一些。

posted @ 2012-09-06 19:57 chen11-1| 编辑 收藏

根据这几条反编译后的语句,怎么找到数据库的连接信息

求:根据这几条反编译后的语句,如何找到数据库的连接信息?
我反编译了一个PBD文件,可以看到以下代码片断:
*******************************
  <0><7> create ()
*******************************



appname = "dossier"
message = create message
sqlca = create transaction
sqlda = create dynamicdescriptionarea
sqlsa = create dynamicstagingarea
error = create error

...
uf_window_center(this)
ls_dsn = profilestring("Dagl.ini","tbdatabase","DSN","Error")
..
dagl.ini文件的内容如下:
[system]
AppName="*****"

[database]
DSN=Dagl
LogId=admin

[content]
PaperSize=256
Left=2350
Right=1340
Top=950
Bottom=850

[Post]
PaperSize=256
Left=2500
Right=1940
Top=1530
Bottom=1260

[gz]
PaperSize=256
Left=2000
Right=910
Top=1400
Bottom=1080

我在哪里能找到连接该数据库的连接代码吗?
profilestring

------解决方案--------------------------------------------------------
ODBC吧,看一下ODBC里有没有Dagl。要么注册表里找一下。
------解决方案--------------------------------------------------------
要是看连接该数据库的连接代码就要找 connect using sqlca
要是看数据库连接参数就要通过ODBC,注意ODBC里的用户DSN和系统DSN都要看一下。
顺便问一句用的是什么数据库?

posted @ 2012-09-06 19:56 chen11-1| 编辑 收藏

怎么让multilineedit滚动到最前面

如何让multilineedit滚动到最前面
mle_1.scroll(1)可以实现逐行往下滚动,那请问如何让multilineedit滚动到最前面?


------解决方案--------------------------------------------------------
editname.Scroll(1)就是将multilineedit滚动第一行的,中间的参数是滚到的行数

posted @ 2012-09-06 19:56 chen11-1 阅读(259) | 评论 (0)编辑 收藏

单个分区索引失效导致绑定变量查询无法使用索引

一个客户碰到的问题,由于分区维护操作,导致个别分区对应的索引处于UNUSABLE状态,最终导致基于绑定变量的查询无法利用索引。

 

 

通过一个具体的例子来说明这个问题:

SQL> create table t_part
2 (id number,
3 name varchar2(30))
4 partition by range (id)
5 (partition p1 values less than (10),
6 partition p2 values less than (20),
7 partition pmax values less than (maxvalue));

Table created.

SQL> create index ind_t_part_id on t_part(id) local;

Index created.

SQL> insert into t_part
2 select rownum, object_name
3 from user_objects;

94 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART', cascade => true)

PL/SQL procedure successfully completed.

SQL> select index_name, partition_name, status
2 from user_ind_partitions
3 where index_name = 'IND_T_PART_ID';

INDEX_NAME PARTITION_NAME STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID P1 USABLE
IND_T_PART_ID P2 USABLE
IND_T_PART_ID PMAX USABLE

创建分区表后,分别采用硬编码和绑定变量的方式进行查询:

SQL> var v_id number
SQL> exec :v_id := 5

PL/SQL procedure successfully tb completed.

SQL> set autot on exp
SQL> select * from t_part where id = 5;

       ID NAME
---------- ------------------------------
        5 WRH$_ACTIVE_SESSION_HISTORY


Execution Plan
----------------------------------------------------------
Plan hash value: 4087175928

--------------------------------------------------------------------------------------------
|Id|Operation                         |Name        |Rows|Bytes|Cost|Time   |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                  |            |  1|  31|  2|00:00:01|     |    |
| 1| PARTITION RANGE SINGLE           |            |  1|  31|  2|00:00:01|   1 |   1|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART      |  1|  31|  2|00:00:01|   1 |   1|
|*3|  INDEX RANGE SCAN               |IND_T_PART_ID|  1|    |  1|00:00:01|   1 |   1|
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  3 - access("ID"=5)

SQL> select * from t_part where id = :v_id;

       ID NAME
---------- ------------------------------
        5 WRH$_ACTIVE_SESSION_HISTORY


Execution Plan
----------------------------------------------------------
Plan hash value: 2089936139

--------------------------------------------------------------------------------------------
|Id|Operation                         |Name        |Rows|Bytes|Cost|Time   |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                  |            |  1|  17|  2|00:00:01|     |    |
| 1| PARTITION RANGE SINGLE           |            |  1|  17|  2|00:00:01| KEY | KEY|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART      |  1|  17|  2|00:00:01| KEY | KEY|
|*3|  INDEX RANGE SCAN               |IND_T_PART_ID|  1|    |  1|00:00:01| KEY | KEY|
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  3 - access("ID"=TO_NUMBER(:V_ID))

无论采用那种方式,Oracle都会选择分区索引扫描的执行计划。

下面MOVE一个查询并不会访问的分区,使其索引状态变为UNUSABLE:

SQL> alter table t_part move partition p2;

Table altered.

SQL> set autot off
SQL> select index_name, partition_name, status
 2 from user_ind_partitions
 3 where index_name = 'IND_T_PART_ID';

INDEX_NAME                    PARTITION_NAME                STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID                 P1                            USABLE
IND_T_PART_ID                 P2                            UNUSABLE
IND_T_PART_ID                 PMAX                          USABLE

SQL> set autot on exp
SQL> select * from t_part where id = 5;

       ID NAME
---------- ------------------------------
        5 WRH$_ACTIVE_SESSION_HISTORY


Execution Plan
----------------------------------------------------------
Plan hash value: 4087175928

--------------------------------------------------------------------------------------------

|Id|Operation                         |Name        |Rows|Bytes|Cost|Time   |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                  |            |  1|  31|  2|00:00:01|     |    |
| 1| PARTITION RANGE SINGLE           |            |  1|  31|  2|00:00:01|   1 |   1|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART      |  1|  31|  2|00:00:01|   1 |   1|
|*3|  INDEX RANGE SCAN               |IND_T_PART_ID|  1|    |  1|00:00:01|   1 |   1|
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  3 - access("ID"=5)

SQL> select * from t_part where id = :v_id;

       ID NAME
---------- ------------------------------
        5 WRH$_ACTIVE_SESSION_HISTORY


Execution Plan
----------------------------------------------------------
Plan hash value: 1818654859

--------------------------------------------------------------------------------------------
| Id| Operation             | Name  | Rows | Bytes |Cost(%CPU)| Time    | Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0| SELECT STATEMENT      |       |   1 |   17 |   2 (0)| 00:00:01 |      |      |
| 1| PARTITION RANGE SINGLE|       |   1 |   17 |   2 (0)| 00:00:01 |  KEY |  KEY |
|* 2|  TABLE ACCESS FULL   | T_PART |   1 |   17 |   2 (0)| 00:00:01 |  KEY |  KEY |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  2 - filter("ID"=TO_NUMBER(:V_ID))

可以看到,对应非绑定变量方式,Oracle是可以明确定位到要访问的分区,因此SQL执行计划不受影响,仍然是索引扫描。而对于绑定变量的方式则不同,由于这个执行计划对于任何一个输入值都要采用相同的计划,因此Oracle无法判断一个查询是否会访问分区索引UNUSABLE的分区,所以Oracle对于绑定变量的查询采用了单分区的全表扫描执行计划。

为了解决这个问题,除了REBUILD失效的分区外,还可以采用HINT的方式,强制Oracle选择索引扫描的执行计划:

SQL> select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id;

       ID NAME
---------- ------------------------------
        5 WRH$_ACTIVE_SESSION_HISTORY


Execution Plan
----------------------------------------------------------
Plan hash value: 2089936139

--------------------------------------------------------------------------------------------
|Id|Operation                         |Name        |Rows|Bytes|Cost|Time   |Pstart|Pstop|
--------------------------------------------------------------------------------------------
| 0|SELECT STATEMENT                  |            |  1|  17|  2|00:00:01|     |    |
| 1| PARTITION RANGE SINGLE           |            |  1|  17|  2|00:00:01| KEY | KEY|
| 2| TABLE ACCESS BY LOCAL INDEX ROWID|T_PART      |  1|  17|  2|00:00:01| KEY | KEY|
|*3|  INDEX RANGE SCAN               |IND_T_PART_ID|  1|    |  1|00:00:01| KEY | KEY|
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  3 - access("ID"=TO_NUMBER(:V_ID))

SQL> exec :v_id := 15

PL/SQL procedure successfully completed.

SQL> select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id;
select /*+ index(t_part ind_t_part_id) */ * from t_part where id = :v_id
*
ERROR at line 1:
ORA-01502: index 'TEST.IND_T_PART_ID' or partition of such index is in unusable state


SQL> select * from t_part where id = :v_id;

       ID NAME
---------- ------------------------------
       15 WRH$_ACTIVE_SESSION_HISTORY_PK


Execution Plan
----------------------------------------------------------
Plan hash value: 1818654859

--------------------------------------------------------------------------------------------
| Id | Operation             | Name  | Rows | Bytes |Cost(%CPU)| Time    |Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT      |       |   1 |   17 |   2 (0)| 00:00:01 |     |      |
| 1 | PARTITION RANGE SINGLE|       |   1 |   17 |   2 (0)| 00:00:01 | KEY |  KEY |
|* 2 |  TABLE ACCESS FULL   | T_PART |   1 |   17 |   2 (0)| 00:00:01 | KEY |  KEY |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

  2 - filter("ID"=TO_NUMBER(:V_ID))

虽然使用HINT可以让Oracle强制索引扫描,但是如果绑定变量的值指向失效的索引分区,则会导致执行报错。而默认的不使用HINT的语句则不会报错。

posted @ 2012-09-05 11:45 chen11-1| 编辑 收藏

设置全局死锁优先级

测试控制全局死锁的隐含参数_lm_dd_interval时,突然想到这个问题。

RAC全局死锁检测时间:http://yangtingkun.net/?p=955

 

 

Oracle的死锁判断是没有优先级的,也就是说,当两个或多个会话发生死锁的时候,无法指定牺牲哪个会话,而是由Oracle随机决定。

不过对于RAC环境而言,死锁的检查不在是内部的随机实现,Oracle通过隐含参数_lm_dd_interval来控制死锁的检测时间。更重要的是,对于RAC环境而言,Oracle允许不同实例设置不同的值。而不同实例的检测死锁间隔不同,就意味着优先级的出现。

如果实例1上设置该值为默认值60秒,而实例2设置为30秒,那么当发生死锁后,永远是实例2上先检测到死锁,也就是说,实例2上会话会被牺牲掉。

这是两个实例上设置该参数相同的情况,两个会话分别连接到两个实例,产生死锁。实例1上的会话1:

SQL> select name from v$database;

NAME
---------
ORCL

SQL> select instance_number, instance_name from v$instance;

INSTANCE_NUMBER INSTANCE_NAME
--------------- ----------------
              1 orcl1

SQL> set sqlp 'I1S1> '
I1S1> show parameter _lm

NAME                                 TYPE                                 VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval                      integer                                 30
I1S1> set timing on
I1S1> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

Elapsed: 00:00:00.07

在实例2上连接会话2:

SQL> select name from v$database;

NAME
---------
ORCL

SQL> select instance_number, instance_name from v$instance;

INSTANCE_NUMBER INSTANCE_NAME
--------------- ----------------
              2 orcl2

SQL> set sqlp 'I2S2> '
I2S2> show parameter _lm

NAME                                 TYPE                                 VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval                      integer                                 30
I2S2> set timing on
I2S2> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.04
I2S2> update t_deadlock set name = 'a2' where id = 1;

会话1上锁定记录2,产生死锁:

I1S1> update t_deadlock set name = 'b1' where id = 2;

第一次是实例2上的会话2被牺牲报错:

update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:32.15
I2S2> update t_deadlock set name = 'a2'  where id = 1;

可以看到,会话2等待30秒后报错,此时会话2执行同样的语句再次引发死锁:

update t_deadlock set name = 'b1' where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:01:00.39
I1S1> update t_deadlock set name = 'b1' where id = 2;

这次变成实例1上的会话1被牺牲报错,可以看到tb,会话1经历了两次死锁检测,因此执行时间为1分钟。会话1再次引入死锁:

update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:01:01.69
I2S2>

被牺牲的又变成了会话2。

上面这个测试是在两个实例的_lm_dd_interval参数设置相同的情况下,下面修改实例2上的参数设置为5秒:

I2S2> alter system set "_lm_dd_interval" = 5 scope = spfile sid = 'orcl2';

System altered.

Elapsed: 00:00:00.09
I2S2> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
I2S2> startup
ORACLE instance started.

Total System Global Area 281018368 bytes
Fixed Size 2095672 bytes
Variable Size 121636296 bytes
Database Buffers 150994944 bytes
Redo Buffers 6291456 bytes
Database mounted.
Database opened.
I2S2> show parameter _lm

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
_lm_dd_interval                      integer     5
I2S2> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.06

实例2参数生效后连接会话更新该表,实例1上的会话1取消之前的修改,重新进行更新:


1 row updated.

Elapsed: 00:10:08.98
I1S1> rollback;

Rollback complete.

Elapsed: 00:00:00.00
I1S1> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

Elapsed: 00:00:00.01
I1S1> update t_deadlock set name = 'b1' where id = 2;

下面在实例2上的会话2,引入死锁:

I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:06.07
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:05.95
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:06.63
I2S2> update t_deadlock set name = 'a2' where id = 1;
update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:05.89

显然由于不同实例的_lm_dd_interval参数的值设置不同,现在每次死锁都会在设置值更小的实例2上被检测,实例2上的会话每次都会被死锁牺牲掉。尝试设置不同的参数值在不同实例上设置死锁检测优先级获得成功。

posted @ 2012-09-05 11:45 chen11-1| 编辑 收藏

对象相关开发手册总结

从这篇文档中学到了不少语法和功能。

 

 

虽然对于TYPE以及COLLECTION类型经常使用,但是涉及到把这些类型作为表的存储结构接触的就少多了,而如果说在加上继承等告警特性,平常使用的就更少了。

在Oracle使用对象一般而言是为了简化某些工作,而把所有面向对象的语法和功能都搬到数据库中不太现实,而且一旦TYPE被应用到表结构中,就很难再进行改变,因此数据库中使用TYPE有一定的局限性。

不过对于一些语法和功能还是有必要知道的,TB或者至少在碰到问题时,应该知道查看那篇文档。

 


posted @ 2012-09-05 11:43 chen11-1| 编辑 收藏

HASH分区新增分区对索引状态的影响

一直认为Oracle对于所有分区的操作都是一样的,只有数据的改变才会导致分区状态的失效,没想到HASH分区的实现方式并不相同。

HASH分区表增加新的分区的一点研究: 

 

看一个范围分区SPLIT的例子:

SQL> CREATE TABLE T_PART
 2 (ID NUMBER, NAME VARCHAR2(30))
 3 PARTITION BY RANGE (ID)
 4 (PARTITION P1 VALUES LESS THAN (10),
 5 PARTITION PMAX VALUES LESS THAN (MAXVALUE));

Table created.

SQL> INSERT INTO T_PART
 2 SELECT ROWNUM, TNAME 
 3 FROM TAB;

12 rows created.

SQL> CREATE INDEX IND_T_PART_ID ON T_PART(ID) LOCAL;

Index created.tb

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_PART_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID                  P1                             USABLE
IND_T_PART_ID                  PMAX                           USABLE

SQL> SELECT COUNT(*) FROM T_PART PARTITION (PMAX);

 COUNT(*)
----------
         3

SQL> ALTER TABLE T_PART SPLIT PARTITION PMAX AT (20)
 2 INTO (PARTITION P2, PARTITION P3);

Table altered.

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_PART_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_PART_ID                  P2                             USABLE
IND_T_PART_ID                  P3                             USABLE
IND_T_PART_ID                  P1                             USABLE

可以看到,对于范围分区而言,即使是SPLIT包含数据的分区,只要没有真正导致数据发生变化,就不会导致索引的失效。这里将PMAX分区SPLIT成P2和P3两个分区,其中PMAX中的所有数据都进入P2分区,而P3分区为空,这种情况下没有数据的改变,因此所有分区索引的状态都不会变为UNUSABLE。

但是HASH分区的ADD PARTITION并没有遵守这个规则,事实上对于每次ADD分区,都会导致一个分区的数据发生分裂,而分裂的结果不管原分区的数据是否发生变化,都会导致原分区索引状态变为UNUSABLE,至于新增分区的索引状态,则取决于是否有数据的改变。

SQL> CREATE TABLE T_HASH
 2 (ID NUMBER)
 3 PARTITION BY HASH (ID)
 4 (PARTITION P1,
 5 PARTITION P2,
 6 PARTITION P3,
 7 PARTITION P4);

Table created.

SQL> CREATE INDEX IND_T_HASH_ID ON T_HASH(ID) LOCAL;

Index created.

SQL> INSERT INTO T_HASH SELECT ROWNUM FROM TAB;

12 rows created.

SQL> COMMIT;

Commit complete.

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_HASH_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID                  P1                             USABLE
IND_T_HASH_ID                  P2                             USABLE
IND_T_HASH_ID                  P3                             USABLE
IND_T_HASH_ID                  P4                             USABLE

SQL> SELECT * FROM T_HASH PARTITION (P1);

        ID
----------
         6
        11

SQL> SELECT * FROM T_HASH PARTITION (P2);

        ID
----------
         9
        10
        12

SQL> SELECT * FROM T_HASH PARTITION (P3);

        ID
----------
         2
         5
         8

SQL> SELECT * FROM T_HASH PARTITION (P4);

        ID
----------
         1
         3
         4
         7

下面新增一个PARTITION P5:

SQL> ALTER TABLE T_HASH ADD PARTITION P5;

Table altered.

SQL> SELECT * FROM T_HASH PARTITION (P5);

no rows selected

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_HASH_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID                  P5                             USABLE
IND_T_HASH_ID                  P1                             UNUSABLE
IND_T_HASH_ID                  P2                             USABLE
IND_T_HASH_ID                  P3                            USABLE
IND_T_HASH_ID                  P4                             USABLE

新增的PARTITION P5中并没有任何的数据,也就是说没有任何的数据从P1迁移到P5中,但是查询分区索引的状态发现,P1对应的分区索引状态已经变为UNUSABLE。这和范围分区的处理方式完全不同。而P5分区由于没有任何数据,因此分区状态是USABLE。

SQL> ALTER TABLE T_HASH ADD PARTITION P6;

Table altered.

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_HASH_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID                  P5                             USABLE
IND_T_HASH_ID                  P6                             UNUSABLE
IND_T_HASH_ID                  P1                             UNUSABLE
IND_T_HASH_ID                  P2                             UNUSABLE
IND_T_HASH_ID                  P3                             USABLE
IND_T_HASH_ID                  P4                             USABLE

6 rows selected.

SQL> DELETE T_HASH WHERE ID = 5;        

1 row deleted.

SQL> COMMIT;

Commit complete.

SQL> ALTER TABLE T_HASH ADD PARTITION P7;

Table altered.

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_HASH_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID                  P5                             USABLE
IND_T_HASH_ID                  P6                             UNUSABLE
IND_T_HASH_ID                  P7                             UNUSABLE
IND_T_HASH_ID                  P1                             UNUSABLE
IND_T_HASH_ID                  P2                             UNUSABLE
IND_T_HASH_ID                  P3                             UNUSABLE
IND_T_HASH_ID                  P4                             USABLE

7 rows selected.

SQL> SELECT * FROM T_HASH PARTITION (P3);

no rows selected

SQL> SELECT * FROM T_HASH PARTITION (P7);

        ID
----------
         2
         8

为了更好的说明这个问题,在增加PARTITION P7之前,删除了ID为5的记录,这是增加分区后可以发现,原有的P3已经不包含任何的数据,全部的记录都进入到新增的P7分区,但是无论是P3还是P7,状态都是UNUSABLE。这证明了前面提到的,只要是新增HASH分区,就会导致源分区索引状态变为UNUSABLE,除非是一种情况:源分区本身就没有数据:

SQL> ALTER TABLE T_HASH ADD PARTITION P8;

Table altered.

SQL> SELECT INDEX_NAME, PARTITION_NAME, STATUS                         
 2 FROM USER_IND_PARTITIONS
 3 WHERE INDEX_NAME = 'IND_T_HASH_ID';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
IND_T_HASH_ID                  P5                             USABLE
IND_T_HASH_ID                  P6                             UNUSABLE
IND_T_HASH_ID                  P7                             UNUSABLE
IND_T_HASH_ID                  P1                             UNUSABLE
IND_T_HASH_ID                  P2                             UNUSABLE
IND_T_HASH_ID                  P3                             UNUSABLE
IND_T_HASH_ID                  P4                             USABLE
IND_T_HASH_ID                  P8                             USABLE

8 rows selected.

事实上,对于HASH分区的ADD PARTITION操作,Oracle基本上还是秉承了没有数据变化就不会导致索引失效的思路。唯一的差别在于,对于源分区包含记录的情况,Oracle并没有最后去验证,是否真的发生了数据的迁移。

posted @ 2012-09-05 11:43 chen11-1| 编辑 收藏

参加ACOUG活动

今天是ACOUG活动的日子,也是上半年的最后一天。

 

 

参加ACOUG的人很多,现场估计超过了100人,而且今天到会的ACE也很多,除了tbEygle、Kamus、崔华和刚晋升为ACE的侯圣文之外,还有从成都赶过来的老熊。老熊虽然是第一次见面,此前打交道的次数也不多,但是有种一见如故的感觉,可能和老熊直爽的性格有关。

今天的第一个主题是杨海朝带来的《MySql的那点事儿》。杨海朝是新浪的首席DBA负责新浪微博的后台数据库,他带来的MySql方面的演讲介绍了如何利用mysql处理海量增长的大数据,以及如何满足前端应用的高可用需求。

第二个是Kamus带来的11g新特性。在10g已经推出了正常服务期,12c马上就要推出的今天,仍然有很多DBA对于11g的新特性不是很了解。Kamus的演讲应该算是一个新特性的普及,这次的内容是关于分区和高可用特性方面的新特性。

最后是崔华带来的SQL优化方面的主题,这次的内容是统计信息。崔华的演讲贯彻了他一向的风格,包含了大量的深入信息,并给出了很多Oracle内部算法和公式,而这些公式大部分来自他深入的研究,在其他的地方是无法看到的。

posted @ 2012-09-05 11:41 chen11-1 阅读(317) | 评论 (0)编辑 收藏

RAC全局死锁检测时间

对于单实例数据库而言,死锁的检测在秒级完成,而RAC环境则死锁的检测时间默认达到了1分钟。

 

 

对于单实例环境如果出现了死锁,那么马上其中一个进程就被中止,用户可以快速的得到错误返回。而对于RAC而言,死锁的检测并不是实时完成,而是需要60秒左右的时间。

会话1执行:

SQL> create table t_deadlock (id number primary key, name varchar2(30));

Table created.

Elapsed: 00:00:00.12
SQL> insert into t_deadlock values (1, 'a');

1 row created.

Elapsed: 00:00:00.00
SQL> insert into t_deadlock values (2, 'b');

1 row created.

Elapsed: 00:00:00.00
SQL> commit;

Commit complete.

Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

Elapsed: 00:00:00.00

会话2执行:

SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.00
SQL> update t_deadlock set name = 'a2' where id = 1;

此时,会话2等待会话1的最终操作,下面会话1更新被会话2锁定的行,引发死锁:

SQL> update t_deadlock set name = 'b1' where id = 2;
update t_deadlock set name = 'b1' where id = 2
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:01:00.12

可以看到,死锁的超时检测为1分钟。

而这个死锁的检测时间是可以调整的,Oracle通过隐含参数_lm_dd_interval控制:

SQL> conn / as sysdba
Connected.
SQL> alter system set "_lm_dd_interval" = 30 scope = spfile;

System altered.

SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area 281018368 bytes
Fixed Size 2095672 bytes
Variable Size 104859080 bytes
Database Buffers 167772160 tb bytes
Redo Buffers 6291456 bytes
Database mounted.
Database opened.

再次测试死锁的检测时间,会话1:

SQL> update t_deadlock set name = 'a1' where id = 1;

1 row updated.

SQL> set timing on

会话2执行更新:

SQL> set timing on
SQL> update t_deadlock set name = 'b2' where id = 2;

1 row updated.

Elapsed: 00:00:00.02
SQL> update t_deadlock set name = 'a2' where id = 1;

会话1执行更新引发死锁:

SQL> update t_deadlock set name = 'b1' where id = 2;

大约30秒后,会话2报错ORA-60:

update t_deadlock set name = 'a2' where id = 1
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource


Elapsed: 00:00:30.27

在10.2.0.2版本上,Oracle存在一个bug,允许这个参数设置为0,在10.2.0.3以后,这个bug被修正,如果设置为0后,则数据库无法正常启动:

[oracle@node1 ~]$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.5.0 - Production on Mon Jun 4 07:54:09 2012

Copyright (c) 1982, 2010, Oracle. All Rights Reserved.

Connected to an idle instance.

SQL> startup
ORA-00067: invalid value 0 for parameter _lm_dd_interval; must be at least 1

最后修改隐含参数是Oracle不推荐的,而且修改这个参数势必会影响RAC的正常工作方式导致LDM进程的繁忙度增加,而且可能影响RAC环境的稳定性和可用性。

如果确实对于前台的死锁检查时间要求较高,建议在测试环境中详细测试后再部署到产品环境中。

 


posted @ 2012-09-04 17:08 chen11-1| 编辑 收藏

11g数据泵NETWORK_LINK功能增强

11g增强了数据泵的NETWORK_LINK功能,使得远端的导入或导出也支持分区粒度。

 

 

在10g的时候,数据泵的NETWORK_LINK只支持表粒度:

[orat3@hpserver2 ~]$ expdp test/test network_link=ora10204 dumpfile=tpart.dp logfile=test.log directory=dd tables=t_part:p1

Export: Release 10.2.0.4.0 - 64bit Production on Monday, 4 June, 2012 0:38:31

Copyright (c) 2003, 2007, Oracle. All rights reserved.

Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
ORA-39001: invalid argument value
ORA-39203: Partition selection is not supported over a network link.

很明显10g并不支持通过网络方式的分区选择功能。而且对于分区表而言,无论是导出还是导入,都是已一个整体完成的。

而在11g中,无论是导入还是导出,NETWORK_LINK方式都支持分区粒度:

solaris*orcl-/home/oracle$ expdp test/test network_link=t111g dumpfile=t_part_hash.dp directory=d_output nologfile=y tables=t_part_hash:sys_p61

Export: Release 11.2.0.3.0 - Production on Mon Jun 4 08:24:54 2012

Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.

Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 tb- 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
Starting "TEST"."SYS_EXPORT_TABLE_01": test/******** network_link=t111g dumpfile=t_part_hash.dp directory=d_output nologfile=y tables=t_part_hash:sys_p61
Estimate in progress using BLOCKS method...
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 8 MB
Processing object type TABLE_EXPORT/TABLE/TABLE
. . exported "TEST"."T_PART_HASH":"SYS_P61" 5.476 KB 13 rows
Master table "TEST"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded
******************************************************************************
Dump file set for TEST.SYS_EXPORT_TABLE_01 is:
/home/oracle/t_part_hash.dp
Job "TEST"."SYS_EXPORT_TABLE_01" successfully completed at 08:25:57

这个功能没有在新特性文档中体现,但是却是一个不错的功能提升。

 

posted @ 2012-09-04 16:55 chen11-1 阅读(1328) | 评论 (0)编辑 收藏

Oracle高可用最佳实践

如果需要了解或架构高可用环境,建议看一下这篇文档。

 

 

这篇文档介绍了Oracle高可用相关的知识点,包括ASM、CLUSTER、RAC、DATA GUARD、GOLDENGATE、BACKUP、RESTORE等等,此外还介绍了高可用环境的监控,以及如何利用这些高可用功能来减少维护的停机时间。

从文档的名称高可用最佳实践和文档的页数高,这篇文档中不会包括这些知识点的具体概念,而应该是这些功能在哪些场合使用最合适,以及相互之间如何配合。

照例给出网址:http://www.oracle.com/pls/db112/to_toc?pathname=server.112%2Fe10803%2Ftoc.htm&remark=portal+%28Books%29

 



posted @ 2012-09-04 16:54 chen11-1 阅读(203) | 评论 (0)编辑 收藏

启动取消导致ORA-600(2658)错误

客户数据库在SHUTDOWN的过程中执行了取消操作,导致了ORA-600错误。

 

 

数据库版本为10.2.0.4 RAC for HP-UX,详细错误信息为:

Thu Oct 22 15:05:14 2009
Shutting down instance: further logons disabled
Thu Oct 22 15:05:15 2009
Stopping background process QMNC
Thu Oct 22 15:05:15 2009
Stopping background process CJQ0
Thu Oct 22 15:05:17 2009
Stopping background process MMNL
Thu Oct 22 15:05:18 2009
Stopping background process MMON
Thu Oct 22 15:05:19 2009
Shutting down instance (immediate)
License high water mark = 8
Thu Oct 22 15:05:19 2009
Stopping Job queue slave processes, flags = 7
Thu Oct 22 15:05:19 2009
Job queue slave processes stopped
Thu Oct 22 15:05:20 2009
ALTER DATABASE CLOSE NORMAL
Thu Oct 22 15:05:20 2009
SMON: disabling tx recovery
SMON: disabling cache recovery tb
Thu Oct 22 15:05:21 2009
LGWR: Waiting for ORLs to be archived...
Thu Oct 22 15:05:36 2009
Reconfiguration started (old inc 2, new inc 4)
List of nodes:
0 1
Global Resource Directory frozen
Communication channels reestablished
* domain 0 valid = 1 according to instance 0
Thu Oct 22 15:05:36 2009
Master broadcasted resource hash value bitmaps
Non-local Process blocks cleaned out
Thu Oct 22 15:05:36 2009
LMS 0: 0 GCS shadows cancelled, 0 closed
Thu Oct 22 15:05:36 2009
LMS 1: 0 GCS shadows cancelled, 0 closed
Set master node info
Submitted all remote-enqueue requests
Dwn-cvts replayed, VALBLKs dubious
All grantable enqueues granted
Thu Oct 22 15:05:36 2009
LMS 1: 2008 GCS shadows traversed, 1001 replayed
Thu Oct 22 15:05:36 2009
LMS 0: 1988 GCS shadows traversed, 1034 replayed
Thu Oct 22 15:05:36 2009
Submitted all GCS remote-cache requests
Fix write in gcs resources
Reconfiguration complete
Thu Oct 22 15:05:51 2009
CLOSE: Error 1013 during database close
Thu Oct 22 15:05:51 2009
SMON: enabling cache recovery
SMON: enabling tx recovery
Thu Oct 22 15:05:51 2009
ORA-1013 signalled during: ALTER DATABASE CLOSE NORMAL...
Thu Oct 22 15:05:51 2009
Errors in file /u01/app/oracle/admin/orcl3/bdump/orcl32_lns1_4678.trc:
ORA-00600: internal error code, arguments: [2658], [4], [2], [], [], [], [], []
Thu Oct 22 15:05:52 2009
Trace dumping is performing id=[cdmp_20091022150552]
Thu Oct 22 15:05:52 2009
Errors in file /u01/app/oracle/admin/orcl3/bdump/orcl32_lns1_4678.trc:
ORA-00600: internal error code, arguments: [2658], [4], [2], [], [], [], [], []
Thu Oct 22 15:05:55 2009
LGWR: ORLs successfully archived
Shutting down archive processes
Archiving is disabled
Thu Oct 22 15:06:00 2009
ARCH shutting down
ARC3: Archival stopped
Thu Oct 22 15:06:05 2009
ARCH shutting down
ARC2: Archival stopped
Thu Oct 22 15:06:10 2009
ARCH shutting down
ARC1: Archival stopped
Thu Oct 22 15:06:15 2009
ARCH shutting down
ARC0: Archival stopped
Thu Oct 22 15:06:16 2009
Thread 2 closed at log sequence 23
Successful close of redo thread 2

可以看到,用户在执行了SHUTDOWN IMMEDIATE操作后,执行了CTRL+C取消操作,导致了ORA-1031错误,之后同一时间引发了ORA-600[2658]错误。显然这个错误的产生和用户发出的取消命令直接相关,而且最终这个命令并未生效,数据库成功的被关闭。

在MOS上查询了一下ORA-600[2658]的相关错误,发现多部分错误都出现在7.3和8.1版本上,在9i以后这个错误已经很少见了,从这一点也可以看出,导致这个错误出现的是偶然的因素,那么取消操作显然是导致这个错误的最大可能性。

数据库本身处于关闭过程中,因此这个错误没有影响,可以直接忽略掉。

posted @ 2012-09-04 16:54 chen11-1 阅读(879) | 评论 (0)编辑 收藏

将TIMESTAMP类型的差值转化为秒的方法

两个TIMESTAMP之差得到的是INTERVAL类型,而有时我们只需要得到两个时间相差的秒数,如果变成INTERVAL之后,想要获取这个值会非常麻烦。

 

 

比较常见的方法是使用EXTRACT来抽取获得的INTERVAL类型的日、时、分和秒来分别计算并求和:

SQL> create table t_timestamp (id number, t1 timestamp, t2 timestamp);

Table created.

SQL> insert into t_timestamp
2 values (1, to_timestamp('20120603222324', 'yyyymmddhh24miss'), to_timestamp('20120526152354', 'yyyymmddhh24miss'));

1 row created.

SQL> commit;

Commit complete.

SQL> select t1 - t2 from t_timestamp where id = 1;

T1-T2
---------------------------------------------------------------------------
+000000008 06:59:30.000000

SQL> with t as (select t1 - t2 interval from t_timestamp where id = 1)
2 select extract(day from interval) * 86400
3 + extract(hour from interval) * 3600
4 + extract(minute from interval) * 60
5 + extract(second from interval) interval
6 from t;

INTERVAL
----------
716370

对于不需要考虑毫秒的情况而言,这种计算过于麻烦了,而对于DATE类型而言,tb计算差值非常方便,直接就可以返回两个日期相差的天数,在乘以86400就可以得到结果。

可惜的是,无论是ROUND还是TRUNC参数,都不支持TIMESTAMP类型:

SQL> select trunc(t1, 'ss') from t_timestamp where id = 1;
select trunc(t1, 'ss') from t_timestamp where id = 1
*
ERROR at line 1:
ORA-01899: bad precision specifier


SQL> select round(t1, 'ss') from t_timestamp where id = 1;
select round(t1, 'ss') from t_timestamp where id = 1
*
ERROR at line 1:
ORA-01899: bad precision specifier

其实对于这个问题,最简单的方法是利用隐式转换,变成DATE类型的操作:

SQL> select (t1 - 0 - (t2 - 0)) * 86400 from t_timestamp;

(T1-0-(T2-0))*86400
-------------------
716370

当然最标准的方法还是显示的转换:

SQL> select (cast(t1 as date) - cast(t2 as date)) * 86400 from t_timestamp;

(CAST(T1ASDATE)-CAST(T2ASDATE))*86400
-------------------------------------
716370

显然这种方便比利用EXTRACT要简单得多。

 


posted @ 2012-09-04 16:52 chen11-1| 编辑 收藏

参考手册总结

一直认为这篇文档作为工具书更合适,不过细读的过程中却改变了我的看法。

 

 

之所以这篇文档的优先级放得比较低,和这篇文档的厚度也有关系。不过这1200多页的内容却包含了很多Oracle中最关键的信息。

第一部分就是初始化参数的详细描述,即使这篇文档只包含了这部分内容,也值得一看了。

第二部分是静态数据字典视图,第三部分是动态数据字典视图。这两部分的内容除了作为工具书备查之外,也是补充自己知识全面性的重要文档,Oracle的提供的数据字典覆盖了非常全面的信息,而我们平时只利用了很少的一部分,如果能充分利用这些视图,那么解决问题会更加的轻松。

最后在附录部分还有重要的内容,tb比如所有等待事件的描述。

posted @ 2012-09-03 17:13 chen11-1 阅读(967) | 评论 (0)编辑 收藏

discover_server报错OVMAPI_4010E

在VM Manager中搜索VM Server时出现这个错误。

 

 

按照VM Server以及VM Manager后,通过指定IP地址,让VM Manager自动寻找VM Server,结果JOB运行报错,详细的错误信息为:

Job Construction Phase
----------------------
begin()
Appended operation 'Discover Manager Server
Discover'tb to object 'OVM Foundry : Discover Manager'.
commit()
Completed Step: COMMIT

Objects and Operations
----------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
 Operation: Discover Manager Server Discover

Job Running Phase at 18:05 on Fri, Nov 25, 2011
----------------------------------------------
Job Participants: []

Actioner
--------
Starting operation 'Discover Manager Server Discover' on object 'OVM Foundry : Discover Manager'
Setting Context to model only in job with id=1322215534120
Job Internal Error (Operation)com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
 at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
 at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
 at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
 at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
 at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
 at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
 at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
 at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
 at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
 at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
 at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
 at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
 at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
 at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
 at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
 at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
 ... 24 more

FailedOperationCleanup
----------
Starting failed operation 'Discover Manager Server Discover' cleanup on object 'OVM Foundry : Discover Manager'
Complete rollback operation 'Discover Manager Server Discover' completed with direction=OVM Foundry : Discover Manager

Rollbacker
----------

Objects To Be Rolled Back
-------------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager

Completed Step: ROLLBACK
Job failed commit (internal) due to OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
 at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
 at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
 at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
 at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
 at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
 at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
 at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
 at sun.reflect.GeneratedMethodAccessor1001.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
 at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
 at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
 at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
 at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
 at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
 at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
 at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
 at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
 ... 24 more

----------
End of Job
----------

由于关键性信息确实,所以无法判断导致错误的原因。即使是在metalink或GOOGLE中查询,也得不到任何有价值的信息。

虽然在VM Manager中得不到有意义的信息,但是在VM Server上,却可以得到更详细的信息,通过检查var/log/ovs-agent.log文件,获取到下面的信息:

[2011-04-16 13:21:46 25970] ERROR (OVSAgentServer:108) Unauthorized access attempt from ('10.0.10.173', 59424)!
Traceback (most recent call last):
 File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 103, in do_POST
   auth(username, password)
 File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 42, in auth
   raise Exception('Authorization failed: user does not exist or password error.')
Exception: Authorization failed: user does not exist or password error.
[2011-04-16 13:21:46 25970] INFO (OVSAgentServer:169) code 403, message Unauthorized access attempt from ('10.0.10.173', 59424)!

这次信息就明确多了,显然是由于VM Manager中配置的密码不正确所致,在VM Server上修改oracle用户密码:

[root@server2 ~]# ovs-agent-passwd oracle
Password:
Again:

在搜索VM Server时使用这里修改的密码,VM Manager成功的发现了VM Server信息。

 


posted @ 2012-09-03 17:12 chen11-1| 编辑 收藏

Oracle VM Server安装手册

简单描述一下Oracle VM Server安装过程。

 

 

需要注意,VM 3.0以上版本才支持升级操作,因此在VM 2.2没有办法升级到当前版本,安装VM 3.0将会删除服务器上所有的数据。

将VM Server的光盘放入,并从光盘启动服务器。

在启动界面直输入Enter开始安装过程:

Oracle会提示是否监测截至,这里可以直接SKIP跳过;

键盘选择:选择us;

然后是版权声明,选择Accept后,开始正式的安装步骤;

如果服务器上没有系统,那么会直接进入后面的分区阶段,否则会提示重装系统还是在原有系统上升级;

选择ReInstall后,会显示当前系统磁盘分区信息,首先选择准备进行系统安装的分区,然后选择Remove tb all partitions and create a new default partition layout,Oracle在格式化分区之前会要求再次确认,并询问是否预览分区空间详细配置,可以完全按照默认推荐值安装,因此这里可以跳过,也可以进入到分区空间修改页面进行自定义的修改;

随后选择Boot Loader配置,选择Master Boot Record;

然后选择一个管理网络接口,手工输入IP和掩码,在下一个页面输入网关、DNS信息,接着是主机名信息;

配置服务器所在时区,配置中找不到北京,可以设置Asia/Shanghai代替;

分别输入Agent密码和root密码后,安装操作完成,这是会提示整个安装的日志文件的位置。

在重启界面选择REBOOT,完成整个安装过程。

启动后,进入Oracle VM Server 3.0控制台界面,可以通过Alt + F2进入linux的登录界面。至此VM Server安装完成。

posted @ 2012-09-03 17:10 chen11-1| 编辑 收藏

ORA-600(qersqCloseRem-2)错误

客户的10.2.0.4 RAC for Hp-un环境碰到了这个错误。

 

 

错误信息为:

Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_11261.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM TB
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_32036.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5935.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5026.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_7620.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:08 2012
Trace dumping is performing id=[cdmp_20120229194207]
Wed Feb 29 19:42:17 2012
Trace dumping is performing id=[cdmp_20120229194217]

这个ORA-600[qersqCloseRem-2]错误非常罕见, TB MOS上居然没有任何记载。不过从错误信息进行进一步的分析,这个错误发生在远端数据库的访问异常。

检查进一步的详细信息:

*** 2012-02-29 19:42:05.564
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Current SQL statement for this session:
SELECT ACCESS_LOG_SEQUENCE.NEXTVAL@WEBDB.COM FROM DUAL
----- PL/SQL Call Stack -----
 object     line object
 handle   number name
0x39b5c3720        5 ECOMMERCE.P_USER_AT
----- Call Stack Trace -----
calling             call    entry               argument values in hex     
location            type    point               (? means dubious value)    
-------------------- -------- -------------------- ----------------------------
ksedst()+31         call    ksedst1()           000000000 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
ksedmp()+610        call    ksedst()            000000000 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
ksfdmp()+21         call    ksedmp()            000000003 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
.
.
.
                                                  0059DF200 ? 683F6E400000001 ?
main()+116          call    opimai_real()       000000002 ? 7FBFFFF4E0 ?
                                                  000000004 ? 7FBFFFF478 ?
                                                  0059DF200 ? 683F6E400000001 ?
__libc_start_main() call    main()              000000002 ? 7FBFFFF4E0 ?
+219                                              000000004 ? 7FBFFFF478 ?
                                                  0059DF200 ? 683F6E400000001 ?
_start()+42         call    __libc_start_main() 0007139F8 ? 000000002 ?
                                                  7FBFFFF628 ? 0052B4BD0 ?
                                                  000000000 ? 000000002 ?
 
--------------------- Binary Stack Dump ---------------------

从详细TRACE分析,在问题发生时刻,正在通过数据库链读取远端序列的值。而此时出现的ORA-3113通信错误,多半与远端数据库状态异常有关。

检查远端数据库的告警日志,果然发现在问题出现时刻,数据库状态异常并最终导致了实例重启:

Wed Feb 29 19:39:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:39:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
.
.
.
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:45:26 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:32 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:33 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:34 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:40 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:43 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:44 2012
Errors in file /opt/app/oracle/admin/orcl/bdump/orcl1_asmb_14614.trc:
ORA-15064: communication failure with ASM instance
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:46:44 2012
ASMB: terminating instance due to error 15064
Wed Feb 29 19:46:44 2012
System state dump is made for local instance
System State dumped to trace file /opt/app/oracle/admin/orcl/bdump/orcl1_diag_14555.trc
Wed Feb 29 19:46:47 2012
Shutting down instance (abort)
License high water mark = 1623
Wed Feb 29 19:46:49 2012
Instance terminated by ASMB, pid = 14614
Wed Feb 29 19:46:52 2012
Instance terminated by USER, pid = 3684

显然远端数据库状态异常是这个ORA-600错误的直接原因。

 


posted @ 2012-09-03 17:09 chen11-1 阅读(904) | 评论 (0)编辑 收藏

Oracle 11g R2的四个新增小特性总结

基于版本的重定义

Oracle 11g R2增加了一个强大的新工具,它可以检出应用程序数据库对象的任一版本,不是所有数据库对象都支持版本化管理,但私有的同义词,视图和几乎所有的PL/SQL对象,包括存储过程、函数、类型、类型主体、包、包主体和触发器,版本化管理的真正好处是简化了部署一个修改版本的应用程序代码到生产数据库,如果部署时遇到一系列的错误,可以很容易地将所有影响的对象回滚到上一个版本。

消除了闪回数据归档上的DDL限制

在前一篇文章中,我深入研究了Oracle 11g R1的新特性“闪回数据归档(Flashback Data Archive,FBDA)”,它也被称为“全部召回(Total Recall)”,它只捕获变化的数据,将这些数据放在一套特殊的对象中,它们构成了FBDA,当用户通过闪回版本查询(Flashback Versions query)查询表的历史记录时,Oracle将会直接从数据库的UNDO表空间返回最近变化的数据,从FBDA返回更旧的数据。

虽然这个特性很好,但在早期版本中也有很多限制,包括增加、修改、重命名、删除表的列、truncate表、修改表的约束、以及修改分区表的分区规范,在Oracle 11g R2中,这些限制全部没有了,对于更复杂的DDL操作,如使用DBMS_REDEFINITION包重定义已经存储到FBDA的基础表,Oracle 11g R2提供了新的DBMS_FLASHBACK_ARCHIVE包,存储过程DISASSOCIATE_FBA将会把基础表从FBDA中分离出来,一旦请求的改变完成,存储过程REASSOCIATE_FBA会被用来重新关联修改的表和基础表。

按需创建分段

在之前的版本中,使用CREATE TABLE语句创建表时,会同时自动创建表的初始段,从Oracle 11gR2开始,这个默认的行为有所变化,创建表时不会创建初始段,直到有数据插入到这个表。此外,任何依赖于该表的索引或LOB段也不会创建,直到有数据插入才会创建,表的SEGMENT CREATION DEFERRED存储属性指定了这个默认行为,但可以使用SEGMENT CREATION IMMEDIATE属性覆盖它。

不可用索引大小归零

在重新载入大表时,比如一个有上百万行的数据仓库事实表,要提高这种表的加载速度,最简单的办法是将该表上的所有索引置为不可用,在数据加载完毕后,在重建这些索引,Oracle 11g R2认可了这一做法,并采取了实质性的措施,tb当索引被标记为不可用时,它会自动删除所有索引段。

小结

Oracle 11g R2延续了自Oracle 10g以来令人称道的自我管理,自我调整,自我治愈的特性,这个新版本提供了太多的新特性,有些是迟来的功能,有些是革新,Oracle DBA可以借助这些新特性提高工作效率,成为一名真正的“信息工程师”。

posted @ 2012-08-31 08:10 chen11-1 阅读(284) | 评论 (0)编辑 收藏

Oracle 11g R2令人赞赏的五大新特性

2009年9月Oracle公司发布了期待已久的Oracle 11g R2,本系列文章将给读者一一揭开新版本中的新特性,并会介绍企业如何利用这些新特性将现有的Oracle 9i,10g,11g R1升级到Oracle 11g R2.

经历了难以忍受的长时间等待,Oracle公司突然在9月1发布了Oracle 11g R2,我不得不承认Oracle的保密工作做得多么好,我相信Oracle公司选择这个时候发布时为了刺激参加Oracle OpenWorld 2009大会的人兴奋和期待。

经过四处搜索Oracle 11g R2新特性文档,并体验了新的OUI(Oracle通用安装程序),创建了我的第一个单实例RAC后,我总结一下Oracle 11g R2中我最喜欢的五大新特性。在后面的文章中我将陆续介绍其它新特性,请锁定本系列文章。

NO.1 随处可见的集群

在以前的版本中,Oracle Clusterware必须要独立地安装在它自己的ORACLE HOME中,并且也只能在RAC环境下使用,这一切在Oracle 11g R2得到彻底颠覆,因为在这个版本中支持安装Oracle网格基础架构,而且只需要一个独立的ORACLE HOME,它包括了Oracle Clusterware和Oracle自动存储管理(ASM)。通过升级后的Oracle通用安装程序安装了网格基础架构后,你将会看到一个全新的功能和服务矩阵,我简单地列举几个吧:

单实例RAC(Oracle重启):听起来似乎自相矛盾,但Oracle 11g R2扩展了Oracle Clusterware的功能,为任何单实例提供了高可用特性,本质上是将数据库变成了单实例RAC数据库。Oracle 11g R2中的Oracle重启特性帮助Oracle网格基础架构的高可用服务控制服务器重启时哪一个监听器,ASM实例和数据库应该启动,它完全取代了过去DBA们经常用到的DBSTART脚本。同样,当单个数据库实例崩溃或其它异常终止时,Oracle重启功能会自动监测失效的实例,并自动重启它,就好像是在一个真实的RAC环境中一样。

SRVCTL升级:如果你管理过旧版本的RAC环境,你可能已经熟悉了RAC环境中的维护工具SRVCTL,在11g R2中,该工具被扩展了,现在可以管理单实例RAC,以及监听器和ASM实例。

集群时间同步服务:Oracle 11g R2现在需要在所有RAC节点上配置时间同步,如果你曾经经历过某个节点被驱逐出RAC集群配置,你一定知道其难度有多大,特别是两个服务器的时间不同步和日志文件的时间戳不同步时,Oracle之前的版本借助系统提供的网络时间协议(NTP)同步所有节点时间,但这需要连接到互联网上的标准时间服务器,作为NTP的替代者,Oracle 11g R2提供了一个新的集群时间同步服务,确保集群中的所有节点的时间都保持一致。

网格即插即用:在以前的版本中,配置RAC最复杂的部分是确定和设置所有节点都需要用到的公共ip地址,私有ip地址和虚拟ip地址。为了简化RAC的安装,Oracle 11g R2提供了一个全新的网格名称服务(GNS),它和域名服务器协作,处理每个网格组件的ip地址分配,当集群环境跨越多个数据库时这个新特性极其有用。

干净地卸载RAC组件:如果你曾经尝试过删除多个节点上的所有RAC痕迹,那一定会钟情于这项新特性,在Oracle 11g R2中,所有安装和配置助手,特别是Oracle通用安装程序(OUI),数据库配置助手(DBCA)和网络配置助手(NETCA),都得到了增强,当需要卸载RAC组件时,可以保证卸得干干净净。

NO.2 ASM加入了集群

Oracle 11g R2 ASM充满了许多吸引人的新特性,对于初学者来说,ASM和Oracle 11g R2 Clusterware安装在同一个Oracle Home下,因此消除了之前推荐的冗余Oracle Home安装方法,并且ASM也从DBCA脱离出来了,有了专门的自动存储管理配置助手(ASMCA)。下面是我认为最有趣的ASM新特性:

智能化数据布局:在之前的版本中,要配置ASM磁盘可能需要存储管理员的参与,需要配置磁盘I/O子系统,Oracle 11g R2提供了ASM分配单元,可以直接受益于磁盘外缘柱面,获得更快的速度,可以将数据文件,重做日志文件和控制放在磁盘外缘获得更好的性能。

EM支持工作台扩展:在这个版本中对Oracle 11g R1引入到企业管理控制台中的自动诊断仓库(ADR)进行了扩展,包括支持ASM诊断,将所有诊断信息打包直接发送给Oracle技术支持,以便获得更快速的ASM性能问题解决方案。

ASMCMD增强:自动存储管理命令行实用工具(ASMCMD)也获得了不少增强,包括:

1)启动和停止ASM实例;

2)备份,恢复和维护ASM实例的服务器参数文件(spfile);

3)实用iostat监控ASM磁盘组的性能;

4)维护新的ASM集群文件系统(ACFS)中的磁盘卷,目录和文件存储,我的下一个话题就是它。

NO.3 ACFS – 一个强健的集群文件系统

Oracle之前也发布过集群文件系统(OCFS),之后又发布了增强版OCFS2,它让Oracle RAC实例可以通过共享存储读写数据库文件,重做日志文件和控制文件。

此外,OCFS也允许RAC数据库的Oracle集群注册文件(OCR)和表决磁盘存储在集群文件系统中,在Oracle 10g R2中,这个需求被取消了,OCR文件和表决磁盘可以存储在裸设备或裸块设备中,如果你曾经在原始设备上丢失过这些文件的所有副本,你一定了解要恢复它们是多么繁琐,因此,在Oracle 11g R2中,将不再支持将这些文件存储在裸设备上。

为了提高这些关键文件的存活能力,Oracle 11g R2正式引入了一种新的集群文件系统,称之为ASM集群文件系统(ACFS),在RAC环境中,ACFS可以为OCR文件和表决磁盘提供更好的保护,它允许创建五份OCR文件副本,tb之前的集群文件系统仅允许保存两份OCR文件,一个主OCR,一个镜像OCR,但ACFS不适合单独的RAC环境,除此之外,几乎所有与操作系统和数据相关的文件都可以从ACFS的安全性和文件共享特性受益。

动态卷管理器:Oracle 11g R2提供了一个新的ASM动态卷管理器(ADVM)来配置和维护存储在ACFS文件系统中的文件,使用ADVM可以在ASM磁盘组内构建一个ADVM卷设备,管理存储在ADVM卷设备中的文件,以及按需调整ADVM卷设备空间大小,最重要的是,因为ADVM是构建在ASM文件系统架构之上的,可以保证存储在这些卷中的文件受到良好的保护,不会出现意外丢失,因为ASM提供了类似RAID的磁盘阵列的功能。

文件访问控制:使用传统的Windows风格访问控制列表(ACL)或Unix/Linux下的用户/组/其它访问权限风格为ACFS目录和文件授予读,写和执行权限,可以通过图形化的企业管理控制台或命令行程序ASMCMD管理ACFS目录和文件安全。

文件系统快照(FSS):Oracle 11g R2通过它的文件系统快照(FSS)功能可以对ACFS文件系统执行快照,一个快照是所选ACFS文件系统的一个只读副本,对相同的ACFS,它会自动保留63个独立的ACFS快照,当某个ACFS文件被不经意地更新,删除或其它危险操作时,这个特性非常有用,利用11g R2企业管理控制台或ACFS acfsutil命令行工具可以找出该文件合适的版本并执行恢复。

NO.4 改善的软件安装和打补丁过程

我发现作为一名DBA压力最大的活就是给Oracle数据库打补丁了,如果补丁可能会引入对数据库有害的行为,我不得不花费大量的时间和精力来确定和审核,因此我对Oracle 11g R2中提供的新功能感到很欢喜。

集群验证实用程序集成:从Oracle 10g开始引入了集群验证实用程序(CVU),现在已经完全集成到Oracle通用安装程序(OUI)和其它配置助手(如DBCA,DBUA)中了。

零停机修补的集群:当为Oracle集群打补丁时,Oracle 11g R2在一个不合适的位置升级方式应用补丁,这意味着会有两个Oracle Home,其中一个专门用来存放补丁的,但一次只能激活一个Oracle Home,在Oracle 11g R2中不用再为升级全部关闭Oracle集群了,实现真正的零停机打补丁。

NO.5 DBMS_SCHEDULER升级

古老的DBMS_SCHEDULER包得到了彻底的更新,DBA经常使用这个包来调度作业。

文件监视器:以前的版本无法在批处理过程中检测大多数触发事件,如检测一个文件抵达某个目录,在Oracle 11g R2中,使用新的文件监视器可以缓解这个问题,一旦预期的文件抵达目录,DBMS_SCHEDULER现在就可以检测到了,并在新的对象类型SCHEDULER_FILEWATCHER_RESULT中注册它的到来,它通过新的CREATE_FILE_WATCHER存储过程向DBMS_SCHEDULER发送一个信号触发作业。

内置的email通知:无论何时,DBMS_SCHEDULER调度任务启动、失败或完成时,任务的状态可以立即通过email发送出去,虽然在以前的版本中也能实现这个功能,但要么调用DBMS_MAIL存储过程,要么调用DBMS_SMTP存储过程,现在这个功能合并到DBMS_SCHEDULER中了。

远程作业:DBMS_SCHEDULER现在允许DBA在远程数据库上创建和调度作业,现在我终于可以在生产库PROD03上通过DBMS_SCHEDULER调用生产库DBMS_SCHEDULER上的存储过程执行任务了,这意味着我现在可以在一台数据库上集中tb创建和维护调度任务了。

多作业目标:最后,现在可以在多个数据库实例上同时调度DBMS_SCHEDULER任务了,在RAC环境中,这个特性非常有用,因为我可以利用多个数据库实例将长时间运行任务分成几部分,分别在不同的数据库实例上执行更小的任务。

posted @ 2012-08-31 08:09 chen11-1| 编辑 收藏

详解DB2中自定义XML存储及其使用环境

使用IBM DB2 for z/OS和DB2 for Linux,UNIX和Windows (LUW),那就没有问题,下面让我们一起回顾一下什么时候使用XML存储,以及如何自定义XML存储的一些最佳实践吧!

为了形象地说明,我将使用一个XML文档,内容如下:

  1. <order OrderID="9001" OrderDate="2009-10-18">> 
  2.  <customerID>26914</customerID> 
  3.  <item id="LK-486"> 
  4.   <name>Magictb Potion</name> 
  5.   <size>300ml</size> 
  6.   <price>19.99</price> 
  7.  </item> 
  8.  <item id="VF-145"> 
  9.   <name>Crystal Ball, Deluxe</name> 
  10.   <color>crystal clear</color> 
  11.   <price>295.00</price> 
  12.  </item> 
  13. </order> 


它展示了一个包括订单ID,日期,客户ID和其它条目的订单XML文档,注意有些条目的描述方式有所不同,如size和color。我们假设需要在DB2中管理许多与此类似的XML文档。

如何拆分和重组XML

我在另一篇文章“15个DB2 pureXML性能最佳实践”中谈到了你应该明智地选择文档的粒度,实际上就是要将存储在DB2中的XML文档与应用程序的业务逻辑对象和主要的访问粒度匹配。

在我们的例子中,假设订单变化非常频繁,订单内的条目读取,添加或删除是最关键的操作,需要最佳的性能,在这种情况下,你可以考虑将订单文档拆分,将每一个条目作为一个独立的文档存储到DB2表的每一行中,这个存储方法(与原来的完整存储订单文档的方法相比)的好处是它使得操作所存储的数据更容易,更快速:

 

可以使用单行读取检索一个条目,不用从一个完整的订单文档中抽取条目了;

可以通过删除表中的行简单地从订单中删除一个条目,不再需要操作完整的订单文档;

可以迅速插入一个新条目到订单中,这时也不需要操作完整的订单文档。

这种轻松添加和移除订单条目的功能在DB2 9 for z/OS中尤其有价值,因为这个版本不支持在现有XML文档中添加或删除元素。
下面的代码显示了一个表的定义,以及拆分一个订单文档的INSERT语句,相关的列分别存储订单ID,客户ID,订单日期和一个条目流水号。

  1. CREATE TABLE items(ordID INTEGER, custID INTEGER,  
  2.                                    odate DATE, seqNo INTEGER, item XML);  
  3. INSERT INTO items(ordID, custID, odate, seqno, item)  
  4.  SELECT T.ordID, T.custID, T.odate, T.seqno, XMLDOCUMENT( T.item)  
  5.  FROM 
  6.   XMLTABLE('$d/order/item' PASSING cast(? AS XML) "d" 
  7.    COLUMNS  
  8.     ordID        INTEGER    PATH      '../@OrderID',  
  9.     custID       INTEGER    PATH      '../customerID' 
  10.     odate        DATE       PATH      '../@OrderDate',  
  11.     seqNo        FOR ORDINALITY,  
  12.     item         XML        PATH      '.'AS T; 

条目信息是以XML格式存储的,因为条目可能有不同的元素和属性,如:

 

  1. ORDID     CUSTID     ODATE     SEQNO     ITEM  
  2. -----     -----     ------     -----     -----  
  3. 9001     26914     10/18/2009     1   <item id="LK-486">  
  4.                                         <name>Magic Potion</name>  
  5.                                         <size>300ml</size>  
  6.                                         <price>19.99</price>  
  7.                                       </item>  
  8. 9001     26914     10/18/2009     2   <item id="VF-145">  
  9.                                         <name>Crystal Ball, Deluxe</name>  
  10.                                         <color>crystaltb clear</color>  
  11.                                         <price>295.00</price>  
  12.                                       </item>  
  13. 2 record(s) selected. 

INSERT语句包括一个XMLTABLE函数,这个函数从输入XML文档抽取插入items表中的值,它将会拆分输入XML文档,生成独立条目的文档。XMLTABLE函数包括一个参数,通过它,应用程序可以传递一个订单文档,使用XPath表达式$d/order/item,XMLTABLE函数为输入文档的每一个条目生成一行数据,然后抽取订单ID,客户ID和订单日期,特殊的列定义FOR ORDINALITY为产生的每一行打上编号。XMLDOCUMENT函数确保每一个条目片段可以作为一个独立的XML文档插入。

上面的代码显示了使用INSERT语句插入XML文档后items表中的数据,下面的代码显示了如何重建原始的订单文档,XMLELEMENT和XMLATTRIBUTES函数使用items表中相关列的值构建的顶部文档,XMLAGG函数组合所有条目,最后形成一个完整的订单文档。注意,XMLAGG在seqno列上包括一个可选的ORDER BY子句,这样可以确保还原后的订单文档和原始文档中的条目显示顺序是一致的。

 

  1. SELECT XMLELEMENT(name "order",  
  2.          XMLATTRIBUTES(ordID AS "OrderID", odate as "OrderDate"),  
  3.          XMLELEMENT(name "customerID", custID)  
  4.          XMLAGG(item ORDER BY seqno) )  
  5. FROM items  
  6. WHERE ordID = 9001  
  7. GROUP BY ordID, odate, custID; 

使用生成列

DB2 9.7 for LUW中新的IBM DB2 pureXML特性允许你与数据库分区功能(Database Partitioning Feature,DPF),范围分区表和多维集群(MDC)表一起使用XML列,但分区或集群键必须由相关的列组成。前面你已经看到了如何使用INSERT和XMLTABLE从XML文档抽取值到相关的列中,你可以使用这些关联列对表进行分区或集群。如果你更喜欢在程序中使用简单的INSERT语句,并且不知道如何抽取数据时,那你可以考虑使用一个生成的列。

DB2 9.7在用户定义函数(UDF)中支持XML参数,允许你定义生成的列,使用插入的XML文档中的值自动填充。下面的代码显示了一个UDF,它接受一个XML文档作为输入,如前面例子中的订单文档,这个UDF使用XMLCAST和XMLQUERY函数抽取输入文档的OrderDate属性:

 

  1. CREATE FUNCTION extractDate(doc XML)  
  2.   RETURNS DATE 
  3.   LANGUAGE SQL CONTAINS SQL  
  4.   NO EXTERNAL ACTION DETERMINISTIC  
  5.   RETURN XMLCAST(XMLQUERY('$d/order/@OrderDate' 
  6.          PASSING doc AS "d"AS DATE); 

你可以在SELECT查询和其它SQL语句中使用这个UDF,但也要定义一个生成列,对于下面的示例,假设检索和插入完整的订单是最关键的操作,在这种情况下,完整地存储订单文档是最好的选择。下面的代码定义了一个使用XML列存储订单的表,并自动抽取订单日期填充到关联的列(odate)中。一条INSERT语句现在可以简单地插入一个XML文档到order列中,不需要考虑抽取值到关联列中:

  1. CREATE TABLE orders(  
  2.   order XML,  
  3.   odate DATE GENERATED ALWAYS AS (extractDate(order))); 

如果你连续不断地存储许多订单,可能需要对旧订单进行归档,这个时候使用范围分区是最好的选择,下面的代码显示了表order2是通过按odate列的值进行分区的,odate列则产生自XML列,同样,你可以使用生成的列作为分区数据库的分配键,也可以作为MDC表的集群键:

  1. CREATE TABLE order2(  
  2.   order XML,  
  3.   odate DATE GENERATED ALWAYS AS (extractDate(order)) NOT NULL)  
  4.  PARTITION BY RANGE (odate)  
  5.  (PART q109 STARTING('01-01-2009') ENDING ('03-31-2009') INCLUSIVE,  
  6.  PART q209 ENDING ('06-30-2009') INCLUSIVE,  
  7.  PART q309 ENDING ('09-30-2009') INCLUSIVE,  
  8.  PART q409 ENDING ('12-31-2009') INCLUSIVE); 

控制XML存储

 

自定义XML存储有许多好处,将大型XML文档拆分成多个小文档,将会使操作XML数据变得更加容易和高效,使用UDF定义生成列可以简化XML值抽取到关联列,使用生成列还可以帮助你管理分区数据库,范围分区表,或MDC表中的XML。

原文出处:http://www.ibm.com/developerworks/data/library/dmmag/DMMag_2009_Issue3/Tips/index.html

原文名:Customizing XML storage in DB2

作者:Matthias Nicola

 

posted @ 2012-08-31 08:08 chen11-1 阅读(1859) | 评论 (0)编辑 收藏

ORA-1595和ORA-1594错误

Oracle 9i上使用自动管理回滚的错误,简单记录一下。

 

 

错误信息为:

Sat May 12 21:54:17 2012
Errors in file /oracle/app/admin/prmdb/bdump/prmdb2_smon_483522.trc:
ORA-01595: error freeing extent (2) of rollback segment (19))
ORA-01594: attempt to wrap into rollback segment (19) extent (2) which is being freed

数据库环境为9208 RAC for Aix,tb跟进MOS文档With AUM Enabled ORA-01594 and ORA-01595 Found in the alert.log [ID 280151.1]的描述,导致问题的原因是在自动回滚管理系统中,如果SMON在尝试收缩一个回滚段时,有新的事务导致回滚段需要扩展,那么这个回收的操作就会报错。因此,可以认为这是一个正常的信息,而非是错误提示,可以简单的忽略这个问题。

Oracle在10g中已经解决了这个问题,在9i中,可以尝试添加更多的回滚空间来解决问题。

 


posted @ 2012-08-30 13:10 chen11-1 阅读(874) | 评论 (0)编辑 收藏

ORA-600(1265)错误

客户的数据库出现ORA-600错误,错误函数为1265。

 

 

数据库版本为10.2.0.4 for Linux,错误信息为:

Fri Aug 26 22:00:11 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
Fri Aug 26 22:00:13 2011
Errors in file /opt/app/
oracletb/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
Fri Aug 26 22:00:13 2011
Trace dumping is performing id=[cdmp_20110826220013]
Fri Aug 26 22:00:14 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-06512: at "USER1.P_PRO", line 5
ORA-04088: error during execution of trigger 'USER1.P_PRO'
Fri Aug 26 22:00:15 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-06512: at "USER1.P_PRO ", line 5
ORA-04088: error during execution of trigger 'USER1.P_PRO'
ORA-06512: at "USER1.U_PRO ", line 25
Fri Aug 26 22:00:17 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-02067: transaction or savepoint rollback required
Fri Aug 26 22:00:18 2011
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_16655.trc:
ORA-00600: internal error code, arguments: [17281], [600], [0x2E134EEC0], [], [], [], [], []
ORA-00600: internal error code, arguments: [1265], [0x42180EA78], [], [], [], [], [], []
ORA-02067: transaction or savepoint rollback required

这个错误是ORA-600[1265]错误引发的,随后还出现了ORA-600[17281]、ORA-4088和ORA-2067错误。其中ORA-2067的描述为:

$ oerr ora 2067
02067, 00000, "transaction or savepoint rollback required"
// *Cause: A failure (typically a trigger or stored procedure with multiple
// remote updates) occurred such that the all-or-nothing execution
// of a previous Oracle call cannot be guaranteed.
// *Action: rollback to a previous savepoint or rollback the transaction
// and resubmit.

从这个描述和Oracle的报错信息不难判断,Oracle在通过触发器更新远端表时引发了这个600错误。

根据Oracle的MOS文档Bug 5655419 Distributed transaction hits ORA-600:[1265] or ORA-600:[k2gget: downgrade] in 10.2的描述,这个错误和分布式事务有关,确认影响的版本就是当前环境的10.2.0.4。这个错误的产生一般与窗口维护有关,可以看到问题的发生时刻恰好是22点,从这个时刻开始,Oracle进入维护窗口,进行空间回收统计信息收集等后台工作,显然就是因为窗口的变化导致了这个错误的产生。

Oracle在11.1.0.6中FIXED了这个bug。除了版本升级外,可以考虑将包含分布式事务修改的程序放到远离时间窗口改变时间。

posted @ 2012-08-30 13:09 chen11-1 阅读(1344) | 评论 (0)编辑 收藏

ORA-600(kccida_kccsgfsz)错误

客户数据库10.1.0.4碰到这个ORA-600错误。

 

 

详细错误信息为:

Sat Feb 4 13:04:31 2006
ALTER DATABASE MOUNT
Sat Feb 4 13:04:31 2006
Errors in file /oracle/admin/orcl/bdump/orcl_ckpt_122986.trc:
ORA-00600: internal error code, arguments: [kccida_kccsgfsz], [], [], [], [], [], [], []
Sat Feb 4 13:04:32 2006
Errors in file /oracle/admin/orcl/bdump/orcl_ckpt_122986.trc:
ORA-00600: internal error code, arguments: [kccida_kccsgfsz], [], [], [], [], [], [], []
Sat Feb 4 13:04:32 2006
CKPT: terminating instance tb due to error 470
Instance terminated by CKPT, pid = 122986

查询MOS发现和文档Alter Database Mount Returns ORA-3113 And ORA-600 [kccida_kccsgfsz] [ID 315112.1]描述的问题一致。导致问题的原因是客户在迁移或断电等因素导致控制文件和数据文件的格式不兼容。

在下次重启时,告警日志中出现的下面的信息也说明了这一点:

Sat Feb 4 13:20:15 2006
alter database mount
Sat Feb 4 13:20:15 2006
Controlfile identified with block size 16384

显然导致这个问题的原因和客户之前的恢复或迁移操作有关。如果如bug所述,数据库是直接从其他平台拷贝到当前环境下,那么正确的方法肯定是通过逻辑备份EXP/EXPDP进行数据库的迁移。

而如果和当前的情况类似,由于异常导致控制文件的损坏,可以考虑从备份中进行恢复或直接重建控制文件。

 


posted @ 2012-08-30 13:08 chen11-1 阅读(219) | 评论 (0)编辑 收藏

分区表UNUSED列后的EXCHANGE PARTITION操作

碰到一个有意思的问题,如果分区表执行过SET UNUSED操作,那么是否还可以进行分区的EXCHANGE操作。

 

 

一个简单的测试就可以说明这个问题:

SQL> create table t_part_unused
 2 (id number, name varchar2(30), other varchar2(30))
 3 partition by range (id)
 4 (partition p1 values less than (10),
 5 partition pmax values less than (maxvalue));

Table created.

SQL> insert into t_part_unused
 2 select rownum, table_name, 'abc'
 3 from user_tables;

48 rows created.

SQL> commit;

Commit complete.

SQL> alter table t_part_unused set unused (other);

Table altered.

SQL> desc t_part_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> create table t_temp_unused as
 2 select *
 3 from t_part_unused
 4 where 1 = 2;

Table created.

SQL> desc t_temp_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14097: column type or size mismatch in ALTER tb TABLE EXCHANGE PARTITION


SQL> alter table t_temp_unused add (other varchar2(30));

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14096: tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns


SQL> alter table t_temp_unused set unused (other);

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;

Table altered.

很明显执行了SET UNUSED操作后的表,和普通的表是存在区别的,这种区别导致要求进行EXCHANGE的表必须同样执行SET UNUSED操作,否则就无法执行EXCHANGE的操作。

当目标表中不包含SETE UNUSED的列时,EXCHANGE操作会出现ORA-14097的错误,而如果把列添加到目标表,则会报错ORA-14096,必须在目标表同样对列执行SET UNUSED操作,才能通过EXCHANGE之前的检查。

其实这也不难理解,执行SET UNUSED命令后,数据字典虽然发生了改变,但是tb表上的数据并没有删除,而EXCHANGE操作只是将两个段的数据字典进行互换,因此如果目标表上缺少SET UNUSED列,是无法执行EXCHANGE操作的。

解决问题的方法有两个,第一个就是例子中展示的可以在目标表上建立列然后同样的执行SET UNUSED操作;另外的一个方法就是对于SET UNUSED列执行DROP COLUMN操作,彻底删除该列。

posted @ 2012-08-30 13:07 chen11-1 阅读(822) | 评论 (0)编辑 收藏

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空

Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有正确的显示结果。

 

 

看一个简单的例子:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL> create table t_part (id number, name varchar2(30))
 2 partition by range (id)
 3 (partition p1 values less than (10),
 4 partition p2 values less than (20),
 5 partition pmax values less than (maxvalue));

Table created.

SQL> select table_name, partition_name, stattype_locked from tb all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 STATT
------------------------------ ------------------------------ -----
T_PART
T_PART                         P1
T_PART                         P2
T_PART                         PMAX

SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART', 'P1')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 STATT
------------------------------ ------------------------------ -----
T_PART
T_PART                         P1
T_PART                         P2
T_PART                         PMAX

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12
T_PART                         P1
T_PART                         P2                             16-JUL-12
T_PART                         PMAX                           16-JUL-12

可以看到在10.2环境中,LOCK_PARTITION_STATS过程是正常工作的,但是DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列并没有正确的显示分区被锁定的结果。

而对于表来说,LOCK_TABLE_STATS过程执行后,STATTYPE_LOCKED的结果显示是正常的:

SQL> exec dbms_stats.lock_table_stats(user, 'T_PART')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12 ALL
T_PART                         P1                                       ALL
T_PART                         P2                             16-JUL-12 ALL
T_PART                         PMAX                           16-JUL-12 ALL

这说明在10.2中,Oracle对于分区列的锁定的支持是存在问题的。查询了一下MOS,Oracle将这个问题确认为内部BUG:7240460,这个问题在11.1.0.7中被FIXED。

而在11.2中,这个问题以及不存在了:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL> select owner, table_name, partition_name, stattype_locked
 2 from dba_tab_statistics
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

OWNER      TABLE_NAME   PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST       T_PART
TEST       T_PART       P2
TEST       T_PART       P3
TEST       T_PART       P4
TEST       T_PART       P5
TEST       T_PART       PMAX

6 rows selected.

SQL> exec dbms_stats.lock_partition_stats('TEST', 'T_PART', 'P2')

PL/SQL procedure successfully completed.

SQL> select owner, table_name, partition_name, stattype_locked
 2 from dba_tab_statistics
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

OWNER      TABLE_NAME   PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST       T_PART
TEST       T_PART       P2              ALL
TEST       T_PART       P3
TEST       T_PART       P4
TEST       T_PART       P5
TEST       T_PART       PMAX

6 rows selected

posted @ 2012-08-29 15:27 chen11-1| 编辑 收藏

ORA-4031导致CJQ进程出现ORA-1003错误

客户数据库出现ORA-4031错误,随后出现了大量的ORA-1003和ORA-604错误。

 

 

数据库版本为10.2.0.3 RAC for HP-UX,详细的报错信息为:

Mon Jul 16 15:30:30 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_2389.trc:
ORA-00603: ORACLE server session terminated by fatal error
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select name,online$,contents...","sql area","tmp")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select name,online$,contents...","sql area","tmp")
Mon Jul 16 15:30:32 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_2878.trc:
ORA-00603: ORACLE server session terminated by fatal error
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
Mon Jul 16 15:30:32 2012
Errors in file /u01/app/oracle/admin/ORCL/udump/orcl2_ora_10030.trc:
ORA-00603: ORACLE server session terminated by fatal error tb
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
ORA-00604: error occurred at recursive SQL level 1
ORA-04031: unable to allocate 144 bytes of shared memory ("shared pool","select name,online$,contents...","Typecheck","coldef: qcopCreateCol")
Mon Jul 16 15:30:39 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","select o.owner#,o.name,o.nam...","sql area","kobjn : kkdcchs")
Mon Jul 16 15:30:40 2012
Trace dumping is performing id=[cdmp_20120716153040]
Mon Jul 16 15:32:19 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:33:59 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:35:39 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:37:19 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:39:00 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:40:40 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQ
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: err
Mon Jul 16 15:41:59 2012
Thread 2 advanced to log sequence 61522
Current log# 7 seq# 61522 mem# 0: +ORCL_CTL/orcl/onlinelog/group_7.263.611598065
Mon Jul 16 15:42:20 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:44:00 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
Mon Jul 16 15:45:40 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl2_cjq0_29269.trc:
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQ
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: error occurred at recursive SQL level 2
ORA-01003: no statement parsed
ORA-00604: error occurred at recursive SQL level ORA-00604: err

可以看得,开始还是比较“正常”的ORA-4031错误,但是随着CJQ进程出现了ORA-4031的错误,数据库开始每1分40秒报一次ORA-1003和ORA-604的错误。这里CJQ进程似乎陷入了一个死循环中,持续不停的报错。查询MOS发现,在9.2中Oracle存在类似的BUG:Bug 3289063 ORA-1003 every 5 seconds after CJQ hits ORA-4031。这个bug已经在9.2.0.5和10.1.0.2中被FIXED,但是这个bug的现象和当前的错误完全一致,从这点上看,这个问题显然在10.2.0.3中仍然存在。

Oracle给出的解决方案也很简单,重启CJQ进程既可。除了在操作系统去kill -9杀掉cjq0对应的进程之外,直接通过alter system set job_queue_processes=0,然后在设置回原值,也有可能解决该问题。此外,尽量避免ORA-4031错误的产生是最根本的问题解决之道。

posted @ 2012-08-29 15:17 chen11-1| 编辑 收藏

利用tar向磁带备份恢复文件

可以利用tar命令向磁盘备份文件,同时可以利用tar从磁带中恢复指定文件。

 

 

备份的命令为:

tar cvf /dev/rmt/0m filename…

恢复的命令为:

tar xvf /dev/rmt/0m filename…

从磁带上恢复文件的例子:

> tar xvf /dev/rmt/0m /archive/orcl_exp_report_1.dmp /archive/orcl_exp_report_2.dmp
x /archive/temp_exp/acdb_exp_report_1.dmp, 3198353408 bytes, 6246784 tape blocks
x /archive/temp_exp/acdb_exp_report_2.dmp, 3078950912 bytes, 6013576 tape blocks

tb恢复时指定的文件名必须是备份到磁带的文件名。

 


posted @ 2012-08-29 15:16 chen11-1 阅读(192) | 评论 (0)编辑 收藏

密码即将过期提示的影响

当用户密码即将过期时,在登录时Oracle会提示ORA-28002错误,但是并不会影响正常的登录。

 

 

本来认为这个信息并没有太大的影响,但是没想到这个错误会导致SET AUTOTRACE功能失效:

solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:28 2012

Copyright (c) 1982, 2011, Oracle. tb All rights reserved.

ERROR:
ORA-28002: the password will expire within 1 days

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
ERROR:
ORA-28002: the password will expire within 1 days


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> alter user test identified by test;

User altered.

SQL> set autot on
ERROR:
ORA-24315: illegal attribute type


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:52 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
SQL> insert into t values (1, 'a');

1 row created.


Execution Plan
----------------------------------------------------------

---------------------------------------------------------------------------------
| Id | Operation                | Name | Rows | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |      |     1 |    12 |  tb   1   (0)| 00:00:01 |
|   1 | LOAD TABLE CONVENTIONAL | T    |       |       |            |          |
---------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
         71 recursive calls
          9 db block gets
         75 consistent gets
         10 physical reads
       1284 redo size
        829 bytes sent via SQL*Net to client
        785 bytes received via SQL*Net from client
          3 SQL*Net roundtrips to/from client
          9 sorts (memory)
          0 sorts (disk)
          1 rows processed

很显然,由于ORA-28002错误导致了SET AUTOTRACE ON功能启用时碰到了错误。当修改了当前的用户密码,则ORA-28002不再出现后,SET AUTOTRACE ON的功能恢复正常。

根据上面的信息其实可以判断,在启用SET AUTOTRACE ON功能时,sqlplus会自动创建一个新的会话来记录当前会话的统计信息。

而启用的新的会话会使用当前会话登录时保存的密码来进行登录,因此可以通过下面的例子来验证这个推论:

solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Sun Jul 15 01:28:38 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
SQL> set autot off
SQL> alter user test identified by test1;

User altered.

SQL> set autot on
ERROR:
ORA-01017: invalid username/password; logon denied


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report

果然,在用户登录后,如果当前的密码被修改,是会导致SET AUTOTRACE ON启用时报错不正确的用户名密码错误的。

posted @ 2012-08-29 15:15 chen11-1| 编辑 收藏

分区表部分分区不可用导致统计信息收集失效

一个客户碰到的具体需求,分区表中有些分区所在的表空间被OFFLINE,导致在删除统计信息时报错。

 

 

下面通过例子来说明这个问题:

SQL> create table t_part_read (id number)
2 partition by range (id)
3 (partition p1 values less than (10) tablespace ts1,
4 partition p2 values less than (20) tablespace ts2,
5 partition pmax values less than (maxvalue) tablespace users);

Table created.

SQL> insert into t_part_read select rownum from tab;

54 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')

PL/SQL procedure successfully completed.

SQL> alter tablespace ts1 read only;

Tablespace altered.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')

PL/SQL procedure successfully completed.

SQL> alter tablespace ts1 offline;

Tablespace altered.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;

*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/tbo1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1

如果将表空间只读,并不会影响到表空间上的表或分区的统计信息的收集,因为收集过程只是读取,而收集的结果信息是写到SYSTEM表空间的。

但是如果分区所在的表空间处于OFFLINE状态,那么在统计信息收集的过程中就会报错。

有一个很简单的方法可以解决这个问题,就是将被OFFLINE影响的分区的统计信息锁定,这样Oracle在收集统计信息时就会跳过锁定的分区,通过这个办法就可以避免统计信息收集过程中的报错:

SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART_READ', 'P1')

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;

*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1


SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ', granularity => 'PARTITION')

PL/SQL procedure successfully completed.

即使锁定分区后,尝试收集统计信息仍然报错,这是因为Oracle默认除了要收集分区上的统计信息以外,还要收集表级的统计信息,而这就会造成被OFFLINE影响的分区也要被读取。

解决方法就是在收集统计信息的时候指定收集的粒度是分区,不收集表上的GLOBAL信息。

 


posted @ 2012-08-27 14:36 chen11-1| 编辑 收藏

ORA-600(kffmXpGet)错误

第一次碰到Exadata上的bug。

 

 

数据库环境Exadata V2-2,数据库版本为11.2.0.2,错误信息为:

Wed Apr 25 11:32:35 2012
Errors in file /u01/app/oracle/diag/rdbms/ods/orcl2/trace/orcl2_ora_9495.trc (incident=304808):
ORA-00600: internal error code, arguments: [kffmXpGet], [145], [69784], [], [], [], [], [], [], [], [], []
ORA-03135: connection lost contact
Incident details in: /u01/app/oracle/diag/rdbms/orcl/orcl2/incident/incdir_304808/orcl2_ora_9495_i304808.trc
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
opidcl aborting process tb unknown ospid (9495) as a result of ORA-600
Wed Apr 25 11:32:36 2012
Dumping diagnostic data in directory=[cdmp_20120425113236], requested by (instance=2, sid=9495), summary=[incident=304808].
Wed Apr 25 11:32:36 2012
Sweep [inc][304808]: completed
Sweep [inc2][304808]: completed
Wed Apr 25 11:32:39 2012
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl2/trace/orcl2_pmon_10797.trc (incident=302224):
ORA-00600: internal error code, arguments: [kffmXpGet], [181], [87276], [], [], [], [], [], [], [], [], []
Incident details in: /u01/app/oracle/diag/rdbms/orcl/orcl2/incident/incdir_302224/orcl2_pmon_10797_i302224.trc
Dumping diagnostic data in directory=[cdmp_20120425113240], requested by (instance=2, sid=10797 (PMON)), summary=[incident=302224].
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
Errors in file /u01/app/oracle/diag/rdbms/orcl/orcl2/trace/orcl2_pmon_10797.trc:
ORA-00600: internal error code, arguments: [kffmXpGet], [181], [87276], [], [], [], [], [], [], [], [], []
PMON (ospid: 10797): terminating the instance due to error 472
Wed Apr 25 11:32:41 2012
opiodr aborting process unknown ospid (11248) as a result of ORA-1092
Wed Apr 25 11:32:41 2012
opiodr aborting process unknown ospid (1967) as a result of ORA-1092
Wed Apr 25 11:32:42 2012
ORA-1092 : opitsk aborting process
Wed Apr 25 11:32:43 2012
License high water mark = 573
Instance terminated by PMON, pid = 10797
USER (ospid: 8755): terminating the instance
Instance terminated by USER, pid = 8755

由于ORA-600[kffmXpGet]错误的出现,最终出现了ORA-1092的错误,并致使opitsk进程退出,导致数据库实例崩溃。

Oracle在MOS文档Bug 12387467 instance crash by ORA-600 [kffmxpget]中描述了这个问题,确认影响的版本为11.2.0.1、11.2.0.2、11.2.0.3,Oracle在11.2.0.2的Bundle Patch 16 for Exadata以及11.2.0.3 Bundle Patch 5 for Exadata中fixed了这个问题,Oracle计划在11.2.0.4和12.1中彻底Fixed该问题。

这个错误只会发生在Exadata中,因为导致错误的原因和Smart Scan有关。当会话执行Smart Scan操作时使用了Ctrl + C中止该操作,会导致PMON进程出现这个600错误。Oracle建议在执行数据文件的SHRINK操作前,中止并退出所有执行Exadata Smart Scan的会话。

posted @ 2012-08-27 14:34 chen11-1| 编辑 收藏

数据安全警示录——Oracle DBA手记4

Eygle的新书经历了4个多月的等待,终于面世了。

 

 

我应该是这本书的第一个读者,4个月前Eygle刚刚完成初稿的时候,我就完整的看过一遍了。

我平常看书比较慢,不过那次却看得很快。一方面是由于大部分案例都比较熟悉;另一方面得益于Eygle的文笔,把故障原因、分析过程、解决思路和处理过程描述得非常清晰,给人一种一气呵成的感觉。导致我这个帮忙审稿的,多次都陷入到具体的内容中了,虽然对于我来说看得tb很爽,但是对于审稿而言并不是一件好事。审稿应该始终站在一个中立的角度,而如果在审稿的过程中过于关注内容,就会忽略掉一些细节的问题。好在Eygle对于自己文章的严谨程度很高,因此通篇看完也没有发现多少不妥之处,估计也不会遗漏太多的问题。

书中的所有内容都来自真实的案例,而且其中有三个个重要的案例都是来自2011年12月30日到2011年12月31日这两天。在2012年元旦马上要来临之前,Eygle接连帮助三个客户进行了数据库的恢复,这件事刺激了Eygle,于是元旦回来,Eygle就开始构思并执笔他的新作。一个多月的时间,这本《数据安全警示录》就基本上完成了。

上次看得是电子版,拿到实体书后感觉这次的印刷质量还是很不错的,等有空的话还要再把这本书再看一遍。

 


posted @ 2012-08-27 14:33 chen11-1| 编辑 收藏

ORA-600(kfnsBackground03)错误

客户的数据库出现了ORA-600(kfnsBackground03)错误。

数据库版本为10.2.0.3 RAC for HP-UX 11.23。这个错误在ASM实例和数据库实例都可能出现,如果发生在ASM实例,并不会导致ASM实例的崩溃,但是如果发生在数据库实例,则会导致数据库实例被强制关闭:

Tue May 15 10:28:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:32:50 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:33:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:34:44 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:43:05 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Tue May 15 10:46:13 2012
Errors in file /u01/app/oracle/admin/+ASM/udump/+asm1_ora_18846.trc:
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Tue May  tb 15 10:46:14 2012
Trace dumping is performing id=[cdmp_20120515104614]

 

上面是ASM实例的报错,下面是对应时刻数据库实例的报错:

Tue May 15 10:38:12 2012
kkjcre1p: unable to spawn jobq slave process
Tue May 15 10:38:12 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_cjq0_17957.trc:

Tue May 15 10:42:19 2012
PMON failed to acquire latch, see PMON dump
Tue May 15 10:43:04 2012
found dead shared server 'S006', pid = (90, 4)
Tue May 15 10:43:10 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j000_19938.trc:
ORA-12012: error on auto execute of job 42579
ORA-27468: "EXFSYS.RLM$EVTCLEANUP" is locked by another process
Tue May 15 10:45:06 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j002_23628.trc:
ORA-12012: error on auto execute of job 8888975
ORA-27468: "ORCL.P_DATA_C" is locked by another process
Tue May 15 10:45:10 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_j003_23959.trc:
ORA-12012: error on auto execute of job 8855572
ORA-27468: "ORCL.P_DATA" is locked by another process
Tue May 15 10:46:14 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_asmb_18844.trc:
ORA-15064: communication failure with ASM instance
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Tue May 15 10:46:14 2012
ASMB: terminating instance due to error 15064
Tue May 15 10:46:15 2012
System state dump is made for local instance
System State dumped to trace file /u01/app/oracle/admin/ORCL/bdump/orcl1_diag_17903.trc
Tue May 15 10:46:16 2012
Shutting down instance (abort)
License high water mark = 52

如果从这次数据库的实例崩溃看,问题似乎和主机上的资源耗尽有关。在问题发生之前,数据库实例已经出现了kkjcre1p: unable to spawn jobq slave process和PMON failed to acquire latch的问题。

当时其他时刻出现这个错误时,似乎并没有确定的资源不足的信息:

Sat May 26 09:47:49 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Sat May 26 09:49:44 2012
NOTE: database ORCL1:ORCL failed during msg 19, reply 2
Sat May 26 09:52:23 2012
Errors in file /u01/app/oracle/admin/+ASM/udump/+asm1_ora_21722.trc:
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Sat May 26 09:52:25 2012
Trace dumping is performing id=[cdmp_20120526095225]

对应这个时刻的数据库告警信息为:

Sat May 26 09:52:24 2012
Errors in file /u01/app/oracle/admin/ORCL/bdump/orcl1_asmb_21720.trc:
ORA-15064: communication failure with ASM instance
ORA-00600: internal error code, arguments: [kfnsBackground03], [], [], [], [], [], [], []
Sat May 26 09:52:24 2012
ASMB: terminating instance due to error 15064
Sat May 26 09:52:25 2012
System state dump is made for local instance
System State dumped to trace file /u01/app/oracle/admin/ORCL/bdump/orcl1_diag_20837.trc
Sat May 26 09:52:26 2012
Shutting down instance (abort)
License high water mark = 46
Sat May 26 09:52:30 2012
Instance terminated by ASMB, pid = 21720
Sat May 26 09:52:31 2012
Instance terminated by USER, pid = 536

这次错误的出现并没有任何其他的信息,数据库实例就直接DOWN掉了。不过每次在出现这个错误时,ASM实例上都会存在告警信息:NOTE: database ORCL1:ORCL failed during msg 19, reply 2。这说明ASM实例和数据库的通信存在了问题。kfnsBackground是Kernel Files Network Service Background的缩写。其中MSG 19是指IOSTAT,而reply 2指的是TIMEOUT,这说明ASM在进行io操作是出现了timeout导致了ASM的异常并导致实例的崩溃。

这个错误相对比较罕见,整个METALINK中,只有3篇文章和这个错误相关,其中两篇是和归档路径空间不足导致系统HANG住,最终导致IO的TIMEOUT,并产生了错误;而另外一篇则没有进一步的信息。其中这三次错误对应的版本分别是10.2.0.4 FOR AIX、10.2.0.4 FOR SOLARIS和10.2.0.3 FOR HPUX,这说明这个错误和平台没有关系,但是问题集中在10.2.0.3和10.2.0.4版本上。

 根据上面的分析,应该部署操作系统信息监控工具,以便于随时观察系统资源的使用情况,在出现类似的错误可以进行辅助分析。由于这个问题没有出现在10.2.0.5中的记录,因此把数据库升级到10.2.0.5有可能避开这个问题。

posted @ 2012-08-24 14:40 chen11-1 阅读(316) | 评论 (0)编辑 收藏

DROP PARTITION为什么不进回收站

前几天在给公司的员工讲一个案例的提到这个问题。

 

 

其实当时提到了这个特点,DROP TABLE会进入回收站,但是DROP PARTITION并不会,因此DROP PARTITION之后,数据无法简单的回复,只能通过逻辑或物理备份的方式来进行数据的回复。

SQL> create table t_drop (id number);

Table created.

SQL> drop table t_drop;

Table dropped.

SQL> select object_name, original_name from recyclebin;

OBJECT_NAME ORIGINAL_NAME
------------------------------ --------------------------------
BIN$xJhZqpmfWZXgRDzZK0pZWw==$0 T_DROP

SQL> create table t_part_drop (id number) partition by range (id)
2 (partition p1 values less than (10),
3 partition p2 values less than (20),
4 partition p3 values less than (30),
5 partition pmax values less than (maxvalue));

Table created.

SQL> insert into t_part_drop tb select rownum from user_objects;

176 rows created.

SQL> commit;

Commit complete.

SQL> alter table t_part_drop drop partition p1;

Table altered.

SQL> select object_name, original_name from recyclebin;

OBJECT_NAME ORIGINAL_NAME
------------------------------ --------------------------------
BIN$xJhZqpmfWZXgRDzZK0pZWw==$0 T_DROP

本来只是普及一下这个常识,不过有人问我Oracle为什么没有实现将删除分区放在回收站中。这个问题问的很好,因为如果这个功能很容易实现,那么Oracle肯定早就实现了,而到了11.2中Oracle仍然没有实现这个功能,那么一定说明这个功能不是无法实现,就是实现的困难太大。

回收站的实现并不复杂,当一张表被删除的时候,Oracle没有直接释放表在表空间上的空间占用,而是将表简单的打了个标识,这样在正常查询数据字典时就不会看到这张被删除的表,而如果需要恢复这张表时,只需要将标识位改回来既可。

那么同样是修改数据字典,为什么不能将被删除的分区通过标识的方法放到回收站中呢,这是因为,对于表而言,删除操作是将一个整理完全删除。而对于分区的删除,是删除整体中的一个部分。对于删除这个动作其实并没有太大的影响,但是回收站的功能不是为了删除,而是为了可以快速的恢复。对表而言,直接恢复整体不存在任何的问题,即使同名对象存在,也只需改个名字既可。而对于删除分区的恢复而言, tb 问题就不那么简单了。由于分区表并没有删除,因此这个表仍然可以继续进行操作,虽然某个分区被删除了,但是除非是范围分区中的MAXVALUE分区和列表分区中的DEFAULT分区,否则再插入原分区对应的数据时,并不会报错,而是会插入到其他分区中:

SQL> select * from t_part_drop partition (p2);

       ID
----------
       10
       11
       12
       13
       14
       15
       16
       17
       18
       19

10 rows selected.

SQL> insert into t_part_drop values (5);

1 row created.

SQL> select * from t_part_drop partition (p2);

       ID
----------
       10
       11
       12
       13
       14
       15
       16
       17
       18
       19
        5

11 rows selected.

原表应该插入分区P1的数据,由于分区P1被删除,因此现在满足分区P2的条件,被插入到分区P2中,考虑这种情况下,如果直接恢复P1分区会怎样。

显然这不是一个简单的数据字典的修改就能解决的问题,不但涉及到分区数据改变的问题,还必然会带来全局和本地索引失效的问题,更重要的是,可能带来主键冲突的情况。

这还只是分区表进行了DML的情况,如果删除分区后,分区表又进行了DDL,比如新SPLIT了P1分区,那么删除分区的恢复操作就更无法进行了。

如果一个功能觉得很简单就可以实现,但是Oracle却一直没有实现,那么很可能实现这个功能并不像想象的那么简单。

posted @ 2012-08-24 14:39 chen11-1 阅读(780) | 评论 (0)编辑 收藏

11gr2增强CREATE OR REPLACE TYPE功能

11.2对于CREATE OR REPLACE TYPE语句进行了增加,增加了FORCE选项。

 

 

在11.2之前,只要有其他的表或TYPE依赖了当前对象,这个对象就无法进行REPLACE了:

SQL> create type t_num_tab is table of number;
2 /

Type created.

SQL> create type t_record is object (id number, n_tab t_num_tab);
2 /

Type created.

SQL> create or replace type t_num_tab is table of number(5);
2 /
create or replace type t_num_tab is table of number(5);
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents

这是11.2之前的情况,尝试执行CREATE OR REPLACE将会导致ORA-2303错误,在11.2中Oracle增加了FORCE功能,使得一个仅被TYPE所依赖的对象仍然可以执行REPLACE的操作:

SQL> create or replace type t_num_tab force is tbable of number(5);
2 /

Type created.

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

有意思的是,FORCE语句的位置不对则不会生效,这时同样会报ORA-2303的错误,而并不会导致语句错误:

SQL> create or replace force type t_num_tab is table of number(5);
create or replace force type t_num_tab is table of number(5)
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents


SQL> create or replace type t_num_tab is table of number(5) force;
2 /
create or replace type t_num_tab is table of number(5) force;
*
ERROR at line 1:
ORA-02303: cannot drop or replace a type with type or table dependents

最后这个功能只对被TYPE所依赖的对象有效,一旦对象被表所依赖,则FORCE功能也不起作用:

SQL> create table t_type_tab (id number, c_tab t_num_tab)
2 nested table c_tab store tb as c_tab_tab;

Table created.

SQL> create or replace type t_num_tab force is table of number(6);
2 /
create or replace type t_num_tab force is table of number(6);
*
ERROR at line 1:
ORA-22866: cannot replace a type with table dependents

Oracle的错误信息也变成了ORA-22866。其实这时可以预料的,因为一旦创建了表,就相当于进行了实体化的工作,如果依赖的类型发生了变化,将会影响表中已有的数据的读写。不过其实Oracle可以做到更进一步,就是如果表段没有创建或者表中没有插入数据的情况下,允许对依赖的对象进行修改。

posted @ 2012-08-24 14:37 chen11-1 阅读(1887) | 评论 (0)编辑 收藏

10g RMAN的REDUNDANCY策略改变

最近发现10g的RMAN备份保留REDUNDANCY策略和9i相比发生了改变。

 

 

在Oracle9i中,备份保留策略的REDUNDANCY的值,指的是备份冗余的个数。也就是说,如果REDUNDANCY设置为1,那么Oracle会保留2个备份。

但是在10g以后,REDUNDANCY的值,就是最终备份保留的值,手头没有10g的环境,用11g的rman做了一个例子:

solaris*orcl-/home/oracle$ rman target /

Recovery Manager: Release 11.2.0.3.0 - Production on Sun Jul 8 19:04:43 2012

Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.

connected to target database: ORCL (DBID=1299676637)

RMAN> show retention policy;

using target database control file instead of recovery catalog
RMAN configuration parameters for database with db_unique_name ORCL are:
CONFIGURE RETENTION POLICY TO REDUNDANCY 1; # default

RMAN> backup tablespace ts_32k;

Starting backup at 08-JUL-12
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=180 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying
datbafile(s) in backup set
input datafile file number=00005 name=/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
channel ORA_DISK_1: starting piece 1 at 08-JUL-12
channel ORA_DISK_1: finished piece 1 at 08-JUL-12
piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp tag=TAG20120708T190559 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 08-JUL-12

RMAN> backup tablespace ts_32k;

Starting backup at 08-JUL-12
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datbafile(s) in backup set
input datafile file number=00005 name=/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf
channel ORA_DISK_1: starting piece 1 at 08-JUL-12
channel ORA_DISK_1: finished piece 1 at 08-JUL-12
piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190609_7zltf22b_.bkp tag=TAG20120708T190609 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 08-JUL-12

RMAN> list backup of tablespace ts_32k;


List of Backup Sets
===================


BS Key Type LV Size       Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
20      Full    2.69M      DISK        00:00:01     08-JUL-12     
        BP Key: 20   Status: AVAILABLE Compressed: NO Tag: TAG20120708T190559
        Piece Name: /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp
 List of Datafiles in backup set 20
 File LV Type Ckp SCN    Ckp Time Name
 ---- -- ---- ---------- --------- ----
 5       Full 28932281   08-JUL-12 /u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf

BS Key Type LV Size       Device Type Elapsed Time Completion Time
------- ---- -- ---------- ----------- ------------ ---------------
21      Full    2.69M      DISK        00:00:01     08-JUL-12     
        BP Key: 21   Status: AVAILABLE Compressed: NO Tag: TAG20120708T190609
        Piece Name: /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190609_7zltf22b_.bkp
 List of Datafiles in backup set 21
 File LV Type Ckp SCN    Ckp Time Name
 ---- -- ---- ---------- --------- ----
 5       Full 28932300   08-JUL-12 /u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts_32k_7w1w3zmb_.dbf

RMAN> delete obsolete;

RMAN retention policy will be applied to the command
RMAN retention policy is set to redundancy 1
using channel ORA_DISK_1
Deleting the following obsolete backups and copies:
Type                 Key    Completion Time    Filename/Handle
-------------------- ------ ------------------ --------------------
Backup Set           20     08-JUL-12        
 Backup Piece       20     08-JUL-12          /u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp

Do you really want to delete the above objects (enter YES or NO)? yes
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/ORCL/backupset/2012_07_08/o1_mf_nnndf_TAG20120708T190559_7zltdqxy_.bkp RECID=20 STAMP=788123159
Deleted 1 objects

可以看到,从10g开始设置的REDUNDANCY的值,就是最终备份保留的个数。为了确认这个问题,特意查询了一下9i和10g的官方文档。

9i的说法是:

The REDUNDANCY parameter specifies that any number of backups or copies beyond a specified number need not be retained.

而10g的文档中,该参数的描述变为:

A redundancy-based backup retention policy determines whether a backup is obsolete based on how many backups of a file are currently on disk.

Oracle改变功能的实现很常见,但是没有想到,对于这种细节的定义也会调整。对于熟悉9i备份策略的DBA需要留神,在设置10g以后的RMAN备份保留策略时,需要在9i的基础上增加1。

posted @ 2012-08-24 14:36 chen11-1| 编辑 收藏

9iRAC环境遭遇library cache lock和library cache load lock等待

客户数据库版本为9208 RAC FOR AIX,客户反应系统缓慢,检查告警日志,发现大量Library cache lock和Library cache load lock等待。

 

 

由于客户的原因,这个问题只是远程协助的方式帮忙检查了一下,因此没有留下任何的操作记录,这里只是简单描述一下问题。

客户反应数据库操作响应变慢,平时一个执行很快的基于主键的UPDATE操作也变得异常缓慢,且执行计划本身并未发生改变。

登录数据库后检查两个节点上的告警日志,并未发现任何异常报错。分别检查两个实例的等待信息,发现除了上面提到的大量Library cache lock和Library cache load lock以外,还有明显的gc等待。

但是随后发现,查询V$SESSION和GV$SESSION的结果居然没有区别,接着查询GV$INSTANTBCE视图,发现只有当前的实例存在,而此时恰好连接另一个节点的工具出现了断连,以至于我一度以为另外一个节点上的实例已经DOWN掉,但是随后重新登录到该节点上,发现数据库实例仍然存在,而且登录到数据库实例中也可以进行任何正常的操作。不过发现在当前节点所有的GV$视图都只会返回当前实例的信息,这与另外一个节点的情况完全一样。显然两个节点间的通信出现了问题,当前节点已经不清楚另外一个节点的状态的。

现在再去分析那些等待信息已经没有太多的意义了,因为整个数据库已经处于不正常的状态。不难推断,当前数据库的异常是由于节点间的通信异常导致。由于9i使用的操作系统的CLUSTER,还没有Oracle的clusterware,剩下只能由操作系统或硬件维护人员去进一步跟踪了。

最终数据库和系统在夜间闲时进行了重启操作,重启后数据库恢复正常,GV$视图的结果也恢复了正常。

posted @ 2012-08-23 16:45 chen11-1| 编辑 收藏

DBMS_OUTPUT包无法输出空行

正常情况下,DBMS_OUTPUT包无法直接输出一个空行。

 

 

以前还真没有注意这个问题,前两天想在输出结果的时候进行一下简单的格式化,发现了这个问题:

SQL> set serverout on
SQL> begin
2 dbms_output.put_line('a');
3 dbms_output.put_line(' ');
4 dbms_output.put_line('b');
5 dbms_output.new_line;
6 dbms_output.put_line('c');
7 end;
8 /
a
b
c

PL/SQL procedure successfully completed.

导致问题的原因在于,如果使用DBMS_OUTPUTB包输出的一行都是不可见字符,那么这行内容被DBMS_OUTPUT包忽略掉。

虽然DBMS_OUTPUT包本身并没有提供开关来屏蔽这个属性,不过这个问题依然很容易解决,最简单的方法莫过于直接把回车包含在字符串中:

SQL> begin
2 dbms_output.put_line('a
3
4 b');
5 dbms_output.put_line('
6 c');
7 end;
8 /
a

b

c

PL/SQL procedure successfully completed.

当然这种方法有可能导致PL/SQL代码的可读性变差,也容易影响代码的缩进格式,此外还有一种方式:

SQL> begin
2 dbms_output.put_line('a' || chr(10) || chr(13));
3 dbms_output.put_line('b');
4 dbms_output.put_line(chr(10) || chr(13) || 'c');
5 end;
6 /
a

b

c

PL/SQL procedure successfully completed.

posted @ 2012-08-23 16:40 chen11-1| 编辑 收藏

客户10.2.0.4环境告警日志出现ORA-27468错误。

客户10.2.0.4环境告警日志出现ORA-27468错误。

 

 

详细错误信息为:

Errors in file /u01/app/oracle/admin/orcl/bdump/orcl1_j000_18724.trc:
ORA-12012: error on auto execute of job 42791
ORA-27468: "EXFSYS.RLM$EVTCLEANUP" is locked by another process

导致这个错误的原因在于升级时没有执行catupgrd.sql,而是执行了建库的部分脚本如catalog.sql和catproc.sql,这导致只有CATALOG视图和系统的PACKAGE以及TYPE的版本更新到10.2.0.4,而其他数据库中组件的版本并没有升级,仍然是10.2.0.1。

在MOS文档ORA-12012 ORA-27468: "SYS.PURGE_LOG" is Locked by Another Process [ID 751884.1]中介绍了这个错误,这个问题可能发生在10.2.0.2到10.2.0.5之间,解决问题的方法很简单,在闲时执行catbupgrd.sql,完成升级组件的后续操作既可。

 

posted @ 2012-08-23 16:39 chen11-1| 编辑 收藏

ORA-600(qersqCloseRem-2)错误

客户的10.2.0.4 RAC for Hp-un环境碰到了这个错误。

 

 

错误信息为:

Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_11261.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_32036.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5935.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_5026.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:05 2012
Errors in file /opt/app/oracle/admin/orcl/udump/orcl1_ora_7620.trc:
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:42:08 2012
Trace dumping is performing id=[cdmp_20120229194207]
Wed Feb 29 19:42:17 2012
Trace dumping is performing id=[cdmp_20120229194217]

这个ORA-600[qersqCloseRem-2]错误非常罕见,在MOS上居然没有任何记载。不过从tb错误信息进行进一步的分析,这个错误发生在远端数据库的访问异常。

检查进一步的详细信息:

*** 2012-02-29 19:42:05.564
ksedmp: internal or fatal error
ORA-00600: internal error code, arguments: [qersqCloseRem-2], [Invalid Handle], [], [], [], [], [], []
ORA-02068: following severe error from WEBDB.COM
ORA-03113: end-of-file on communication channel
Current SQL statement for this session:
SELECT ACCESS_LOG_SEQUENCE.NEXTVAL@WEBDB.COM FROM DUAL
----- PL/SQL Call Stack -----
 object     line object
 handle   number name
0x39b5c3720        5 ECOMMERCE.P_USER_AT
----- Call Stack Trace -----
calling             call    entry               argument values in hex     
location            type    point               (? means dubious value)    
-------------------- -------- -------------------- ----------------------------
ksedst()+31         call    ksedst1()           000000000 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
ksedmp()+610        call    ksedst()            000000000 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
ksfdmp()+21         call    ksedmp()            000000003 ? 000000001 ?
                                                  7FBFFF4370 ? 7FBFFF43D0 ?
                                                  7FBFFF4310 ? 000000000 ?
.
.
.
                                                  0059DF200 ? 683F6E400000001 ?
main()+116          call    opimai_real()       000000002 ? 7FBFFFF4E0 ?
                                                  000000004 ? 7FBFFFF478 ?
                                                  0059DF200 ? 683F6E400000001 ?
__libc_start_main() call    main()              000000002 ? 7FBFFFF4E0 ?
+219                                              000000004 ? 7FBFFFF478 ?
                                                  0059DF200 ? 683F6E400000001 ?
_start()+42         call    __libc_start_main() 0007139F8 ? 000000002 ?
                                                  7FBFFFF628 ? 0052B4BD0 ?
                                                  000000000 ? 000000002 ?
 
--------------------- Binary Stack Dump ---------------------

从详细TRACE分析,在问题发生时刻,正在通过数据库链读取远端序列的值。而此时出现的ORA-3113通信错误,多半与远端数据库状态异常有关。

检查远端数据库的告警日志,果然发现在问题出现时刻,数据库状态异常并最终导致了实例重启:

Wed Feb 29 19:39:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:39:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:40:01 2012
WARNING: inbound connection timed out (ORA-3136)
.
.
.
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:28 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:29 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:43:30 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:45:26 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:32 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:33 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:34 2012
PMON failed to acquire latch, see PMON dump
Wed Feb 29 19:46:40 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:43 2012
WARNING: inbound connection timed out (ORA-3136)
Wed Feb 29 19:46:44 2012
Errors in file /opt/app/oracle/admin/orcl/bdump/orcl1_asmb_14614.trc:
ORA-15064: communication failure with ASM instance
ORA-03113: end-of-file on communication channel
Wed Feb 29 19:46:44 2012
ASMB: terminating instance due to error 15064
Wed Feb 29 19:46:44 2012
System state dump is made for local instance
System State dumped to trace file /opt/app/oracle/admin/orcl/bdump/orcl1_diag_14555.trc
Wed Feb 29 19:46:47 2012
Shutting down instance (abort)
License high water mark = 1623
Wed Feb 29 19:46:49 2012
Instance terminated by ASMB, pid = 14614
Wed Feb 29 19:46:52 2012
Instance terminated by USER, pid = 3684

显然远端数据库状态异常是这个ORA-600错误的直接原因。

posted @ 2012-08-22 13:15 chen11-1| 编辑 收藏

Oracle VM Server安装手册

简单描述一下Oracle VM Server安装过程。

 

 

需要注意,VM 3.0以上版本才支持升级操作,因此在VM 2.2没有办法升级到当前版本,安装VM 3.0将会删除服务器上所有的数据。

将VM Server的光盘放入,并从光盘启动服务器。

在启动界面直输入Enter开始安装过程:

Oracle会提示是否监测截至,这里可以直接SKIP跳过;

键盘选择:选择us;

然后是版权声明,选择Accept后,开始正式的安装步骤;

如果服务器上没有系统,那么会直接进入后面的分区阶段,否则会提示重装系统还是在原有系统上升级;

选择ReInstall后,会显示当前系统磁盘分区信息,首先选择准备进行系统安装的分区,然后选择Remove all partitions and create a new defaultb partition layout,Oracle在格式化分区之前会要求再次确认,并询问是否预览分区空间详细配置,可以完全按照默认推荐值安装,因此这里可以跳过,也可以进入到分区空间修改页面进行自定义的修改;

随后选择Boot Loader配置,选择Master Boot Record;

然后选择一个管理网络接口,手工输入IP和掩码,在下一个页面输入网关、DNS信息,接着是主机名信息;

配置服务器所在时区,配置中找不到北京,可以tb设置Asia/Shanghai代替;

分别输入Agent密码和root密码后,安装操作完成,这是会提示整个安装的日志文件的位置。

在重启界面选择REBOOT,完成整个安装过程。

启动后,进入Oracle VM Server 3.0控制台界面,可以通过Alt + F2进入linux的登录界面。至此VM Server安装完成。

 


posted @ 2012-08-22 13:14 chen11-1| 编辑 收藏

discover_server报错OVMAPI_4010E

在VM Manager中搜索VM Server时出现这个错误。

 

 

按照VM Server以及VM Manager后,通过指定IP地址,让VM Manager自动寻找VM Server,结果JOB运行报错,详细的错误信息为:

Job Construction Phase
----------------------
begin()
Appended operation 'Discover Manager Server Discover' to object 'OVM Foundry : Discover Manager'.
commit()
Completed Step: COMMIT

Objects and Operations
----------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager
 Operation: Discover Manager Server Discover

Job Running Phase at 18:05 on Fri, Nov 25, 2011
----------------------------------------------
Job Participants: []

Actioner
--------
Starting operation 'Discover Manager Server Discover' on object 'OVM Foundry : Discover Manager'
Setting Context to model only in job with id=1322215534120
Job Internal Error (Operation)com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
 at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
 at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
 at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
 at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
 at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
 at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
 at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
 at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
 at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
 at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
 at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
 at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
 at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
 at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
 at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
 at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:470)
 ... 24 more

FailedOperationCleanup
----------
Starting failed operation 'Discover Manager Server Discover' cleanup on object 'OVM Foundry : Discover Manager'
Complete rollback operation 'Discover Manager Server Discover' completed with direction=OVM Foundry : Discover Manager

Rollbacker
----------

Objects To Be Rolled Back
-------------------------
Object (IN_USE): [Server] 35:38:33:39:31:34:43:4e:47:31:33:30:53:37:33:42 (server2.zihexin.com)
Object (IN_USE): [DiscoverManager] OVM Foundry : Discover Manager

Completed Step: ROLLBACK
Job failed commit (internal) due to OVMAPI_4010E Attempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
com.oracle.ovm.mgr.api.exception.FailedOperationException: OVMAPI_4010E Atbtempt to send command: discover_server to server: 10.0.10.171 failed. OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActionEngine.java:474)
 at com.oracle.ovm.mgr.action.ActionEngine.sendDiscoverCommand(ActionEngine.java:283)
 at com.oracle.ovm.mgr.action.ServerAction.getServerInfo(ServerAction.java:95)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:131)
 at com.oracle.ovm.mgr.discover.ovm.ServerBasicDiscoverHandler.query(ServerBasicDiscoverHandler.java:61)
 at com.oracle.ovm.mgr.discover.ovm.DiscoverHandler.execute(DiscoverHandler.java:50)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.handleDiscover(DiscoverEngine.java:435)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverNewServer(DiscoverEngine.java:345)
 at com.oracle.ovm.mgr.discover.DiscoverEngine.discoverServer(DiscoverEngine.java:265)
 at com.oracle.ovm.mgr.op.manager.DiscoverManagerServerDiscover.action(DiscoverManagerServerDiscover.java:48)
 at com.oracle.ovm.mgr.api.job.JobEngine.operationActioner(JobEngine.java:191)
 at com.oracle.ovm.mgr.api.job.JobEngine.objectActioner(JobEngine.java:257)
 at com.oracle.ovm.mgr.api.job.InternalJobDbImpl.objectCommitter(InternalJobDbImpl.java:1019)
 at sun.reflect.GeneratedMethodAccessor1001.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at com.oracle.odof.core.AbstractVessel.invokeMethod(AbstractVessel.java:223)
 at com.oracle.odof.core.BasicWork.invokeMethod(BasicWork.java:136)
 at com.oracle.odof.command.InvokeMethodCommand.process(InvokeMethodCommand.java:100)
 at com.oracle.odof.core.BasicWork.processCommand(BasicWork.java:81)
 at com.oracle.odof.core.TransactionManager.processCommand(TransactionManager.java:751)
 at com.oracle.odof.core.WorkflowManager.processCommand(WorkflowManager.java:395)
 at com.oracle.odof.core.WorkflowManager.processWork(WorkflowManager.java:453)
 at com.oracle.odof.io.AbstractClient.run(AbstractClient.java:42)
 at java.lang.Thread.run(Thread.java:662)
Caused by: com.oracle.ovm.mgr.api.exception.IllegalOperationException: OVMAPI_4004E Server Failed Command: discover_server, Status:
Fri Nov 25 18:05:34 CST 2011
 at com.oracle.ovm.mgr.action.ActionEngine.sendAction(ActionEngine.java:752)
 at com.oracle.ovm.mgr.action.ActionEngine.sendCommandToServer(ActbionEngine.java:470)
 ... 24 more

----------
End of Job
----------

由于关键性信息确实,所以无法判断导致错误的原因。即使是在metalink或GOOGLE中查询,也得不到任何有价值的信息。

虽然在VM Manager中得不到有意义的信息,但是在VM Server上,却可以得到更详细的信息,通过检查var/log/ovs-agent.log文件,获取到下面的信息:

[2011-04-16 13:21:46 25970] ERROR (OVSAgentServer:108) Unauthorized access attempt from ('10.0.10.173', 59424)!
Traceback (most recent call last):
 File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 103, in do_POST
   auth(username, password)
 File "/opt/ovs-agent-3.0/OVSAgentServer.py", line 42, in auth
   raise Exception('Authorization failed: user does not exist or password error.')
Exception: Authorization failed: user does not exist or password error.
[2011-04-16 13:21:46 25970] INFO (OVSAgentServer:169) code 403, message Unauthorized access attempt from ('10.0.10.173', 59424)!

这次信息就明确多了,显然是由于VM Manager中配置的密码不正确所致,在VM Server上修改oracle用户密码:

[root@server2 ~]# ovs-agent-passwd oracle
Password:
Again:

在搜索VM Server时使用这里修改的密码,VM Manager成功的发现了VM Server信息。

posted @ 2012-08-22 13:13 chen11-1| 编辑 收藏

分区表部分分区不可用导致统计信息收集失效

一个客户碰到的具体需求,分区表中有些分区所在的表空间被OFFLINE,tb导致在删除统计信息时报错。

 

 

下面通过例子来说明这个问题:

SQL> create table t_part_read (id number)
2 partition by range (id)
3 (partition p1 values less than (10) tablespace ts1,
4 partition p2 values less than (20) tablespace ts2,
5 partition pmax values less than (maxvalue) tablespace users);

Table created.

SQL> insert into t_part_read select rownum from tab;

54 rows created.

SQL> commit;

Commit complete.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')

PL/SQL procedure successfully completed.

SQL> alter tablespace ts1 read only;

Tablespace altered.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')

PL/SQL procedure successfully completed.

SQL> alter tablespace ts1 offline;

Tablespace altered.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;

*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1

如果将表空间只读,并不会影响到表空间上的表或分区的统计信息的收集,因为收集过程只是读取,而收集的结果信息是写到SYSTEM表空间的。

但是如果分区所在的表空间处于OFFLINE状态,那么在统计信息收集的过程中就会报错。

有一个很简单的方法可以解决这个问题,就是将被OFFLINE影响的分区的统计信息锁定,这样Oracle在收集统计信息时就会跳过锁定的分区,通过这个办法就可以避免统计信息收集过程中的报错:

SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART_READ', 'P1')

PL/SQL procedure successfully completed.

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART_READ'); END;

*
ERROR at line 1:
ORA-00376: file 6 cannot be read at this time
ORA-01110: data file 6: '/u01/app/oracle/oradata/ORCL/datafile/o1_mf_ts1_7w8l5fz1_.dbf'
ORA-06512: at "SYS.DBMS_STATS", line 23829
ORA-06512: at "SYS.DBMS_STATS", line 23880
ORA-06512: at line 1


SQL> exec dbms_stats.gather_table_stats(user, 'T_PART_READ', granularity => 'PARTITION')

PL/SQL procedure successfully completed.

即使锁定分区后,尝试收集统计信息仍然报错,这是因为Oracle默认除了要收集分区上的统计信息以外,还要收集表级的统计信息,而这就会造成被OFFLINE影响的分区也要被读取。

解决方法就是在收集统计信息的时候指定收集的粒度是分区,不收集表上的GLOBAL信息。

 


posted @ 2012-08-20 13:11 chen11-1| 编辑 收藏

密码即将过期提示的影响

当用户密码即将过期时,在登录时Oracle会提示ORA-28002错误,但是并不会影响正常的登录。

 

 

本来认为这个信息并没有太大的影响,但是没想到这个tb错误会导致SET AUTOTRACE功能失效:

solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:28 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.

ERROR:
ORA-28002: the password will expire within 1 days

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
ERROR:
ORA-28002: the password will expire within 1 days


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> alter user test identified by test;

User altered.

SQL> set autot on
ERROR:
ORA-24315: illegal attribute type


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options
solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Fri Jul 13 11:27:52 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
SQL> insert into t values (1, 'a');

1 row created.


Execution Plan
----------------------------------------------------------

---------------------------------------------------------------------------------
| Id | Operation                | Name | Rows | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |      |     1 |    12 |     1   (0)| 00:00:01 |
|   1 | LOAD TABLE CONVENTIONAL | T    |       |       |            |          |
---------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
         71 recursive calls
          9 db block gets
         75 consistent gets
         10 physical reads
       1284 redo size
        829 bytes sent via SQL*Net to client
        785 bytes received via SQL*Net from client
          3 SQL*Net roundtrips to/from client
          9 sorts (memory)
          0 sorts (disk)
          1 rows processed

很显然,由于ORA-28002错误导致了SET AUTOTRACE ON功能启用时碰到了错误。当修改了当前的用户密码,则ORA-28002不再出现后,SET AUTOTRACE ON的功能恢复正常。

根据上面的信息其实可以判断,在启用SET AUTOTRACE ON功能时,sqlplus会自动创建一个新的会话来记录当前会话的统计信息。

而启用的新的会话会使用当前会话登录时保存的密码来进行登录,因此可以通过下面的例子来验证这个推论:

solaris*orcl-/home/oracle$ sqlplus test/test

SQL*Plus: Release 11.2.0.3.0 Production on Sun Jul 15 01:28:38 2012

Copyright (c) 1982, 2011, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, Oracle Label Security and Real Application Testing options

SQL> set autot on
SQL> set autot off
SQL> alter user test identified by test1;

User altered.

SQL> set autot on
ERROR:
ORA-01017: invalid username/password; logon denied


SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report

果然,在用户登录后,如果当前的密码被修改,是会导致SET AUTOTRACE ON启用时报错不正确的用户名密码错误的。

 


posted @ 2012-08-20 13:10 chen11-1| 编辑 收藏

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空

 

Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有tb正确的显示结果。

 

 

看一个简单的例子:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE 10.2.0.5.0 Production
TNS for Linux: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL> create table t_part (id number, name varchar2(30))
 2 partition by range (id)
 3 (partition p1 values less than (10),
 4 partition p2 values less than (20),
 5 partition pmax values less than (maxvalue));

Table created.

SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 STATT
------------------------------ ------------------------------ -----
T_PART
T_PART                         P1
T_PART                         P2
T_PART                         PMAX

SQL> exec dbms_stats.lock_partition_stats(user, 'T_PART', 'P1')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 STATT
------------------------------ ------------------------------ -----
T_PART
T_PART                         P1
T_PART                         P2
T_PART                         PMAX

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12
T_PART                         P1
T_PART                         P2                             16-JUL-12
T_PART                         PMAX                           16-JUL-12

可以看到在10.2环境中,LOCK_PARTITION_STATS过程是正常工作的,但是DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列并没有正确的显示分区被锁定的结果。

而对于表来说,LOCK_TABLE_STATS过程执行后,STATTYPE_LOCKED的结果显示是正常的:

SQL> exec dbms_stats.lock_table_stats(user, 'T_PART')

PL/SQL procedure successfully completed.

SQL> select table_name, partition_name, last_analyzed, stattype_locked from all_tab_statistics where wner = user and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12 ALL
T_PART                         P1                                       ALL
T_PART                         P2                             16-JUL-12 ALL
T_PART                         PMAX                           16-JUL-12 ALL

这说明在10.2中,Oracle对于分区列的锁定的支持是存在问题的。查询了一下MOS,Oracle将这个问题确认为内部BUG:7240460,这个问题在11.1.0.7中被FIXED。

而在11.2中,这个问题以及不存在了:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0      Production
TNS for Solaris: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

SQL> select owner, table_name, partition_name, stattype_locked
 2 from dba_tab_statistics
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

OWNER      TABLE_NAME   PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST       T_PART
TEST       T_PART       P2
TEST       T_PART       P3
TEST       T_PART       P4
TEST       T_PART       P5
TEST       T_PART       PMAX

6 rows selected.

SQL> exec dbms_stats.lock_partition_stats('TEST', 'T_PART', 'P2')

PL/SQL procedure successfully completed.

SQL> select owner, table_name, partition_name, stattype_locked
 2 from dba_tab_statistics
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

OWNER      TABLE_NAME   PARTITION_NAME STATT
---------- ------------ --------------- -----
TEST       T_PART
TEST       T_PART       P2              ALL
TEST       T_PART       P3
TEST       T_PART       P4
TEST       T_PART       P5
TEST       T_PART       PMAX

6 rows selected.

 

posted @ 2012-08-20 13:09 chen11-1| 编辑 收藏

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空的解决

Oracle10g的DBA_TAB_STATISTICS视图的STATTYPE_LOCKED列没有正确的显示结果。

10g中DBA_TAB_STATISTICS的STATTYPE_LOCKED列对分区锁定显示为空:http://yangtingkun.net/?p=1023

 

 

上文提到了DBA_TAB_STATISTICS中的STATTYPE_LOCKED列在10g中对于分tb区锁定统计信息显示为空,那么在10g中有没有办法获取到正确的结果呢:

SQL> select table_name, partition_name, last_analyzed, stattype_locked
 2 from dba_tab_statistics
 3 where wner = user
 4 and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 LAST_ANAL STATT
------------------------------ ------------------------------ --------- -----
T_PART                                                        16-JUL-12
T_PART                         P1
T_PART                         P2                             16-JUL-12
T_PART                         PMAX                           16-JUL-12

SQL> exec dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1')
BEGIN dbms_stats.gather_table_stats(user, 'T_PART', partname => 'P1'); END;

*
ERROR at line 1:
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: at "SYS.DBMS_STATS", line 15027
ORA-06512: at "SYS.DBMS_STATS", line 15049
ORA-06512: at line 1

显然虽然Oracle在DBA_TAB_STATISTICS视图中没有正确的显示分区的锁定状态,但是Oracle在内部确实记录了分区的锁定状态,既然Oracle记录了这个信息,就有办法将这个信息显示出来。

既然11g能够显示该列的值,最简单的方法莫过于对比10g和11g中DBA_TAB_STATISTICS视图的区别,10g视图的结果:

SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';

TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
    u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
    decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* PARTITIONS, NOT IOT */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
    sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* IOT Partitions */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m

 WHERE
.
.
.
 UNION ALL
 SELECT /* COMPOSITE PARTITIONS */
    u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
    sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* SUBPARTITIONS */
    u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
    decode(bitand(tab.trigflag, 67108864) + bitand(tab.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
    sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* FIXED TABLES */
    'SYS', t.kqftanam, NULL, NULL, NULL, NULL, 'FIXED TABLE',
.
.
.

对比一下11g的查询结果tb

SQL> select text from dba_views where view_name = 'DBA_TAB_STATISTICS';

TEXT
--------------------------------------------------------------------------------
SELECT /* TABLES */
    u.name, o.name, NULL, NULL, NULL, NULL, 'TABLE', t.rowcnt,
.
.
.
    decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
           0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tab$ t, sys.tab_stats$ ts, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* PARTITIONS, NOT IOT */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab_stats$ ts, sys.tab$ tab,
    sys.mon_mods_all$ m
.
.
.
 UNION ALL
 SELECT /* IOT Partitions */
    u.name, o.name, o.subname, tp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab, sys.mon_mods_all$ m

 WHERE
.
.
.
 UNION ALL
 SELECT /* COMPOSITE PARTITIONS */
    u.name, o.name, o.subname, tcp.part#, NULL, NULL, 'PARTITION',
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp,
    sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* SUBPARTITIONS */
    u.name, po.name, po.subname, tcp.part#, so.subname, tsp.subpart#,
.
.
.
    decode(
      /*
       * Following decode returns 1 if DATA stats locked for partition
       * or at table level.
       * Note that dbms_stats does n't allow locking subpartition stats.
       * If the composite partition is locked, all subpartitions are
       * considered locked. Hence decode checks for tcp entry.
       */
      decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
      /*
       * Following decode returns 2 if CACHE stats locked for partition
       * or at table level
       */
      decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
      /* if 0 => not locked, 3 => data and cache stats locked */
      0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL'),
.
.
.
 FROM
    sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp,
    sys.tabsubpartv$ tsp, sys.tab_stats$ ts, sys.tab$ tab, sys.mon_mods_all$ m
 WHERE
.
.
.
 UNION ALL
 SELECT /* FIXED TABLES */
.
.

显然在11g中Oracle对于分区锁定的显示采用了新的算法,那么可以仿照11g中建立一个视图,来解决10g中分区显示存在错误的问题:

SQL> CREATE OR REPLACE VIEW DBA_TAB_STATISTICS_LOCK
 2 (OWNER, TABLE_NAME, PARTITION_NAME,
 3 SUBPARTITION_NAME, OBJECT_TYPE, STATTYPE_LOCKED)
 4 AS
 5 SELECT u.name, o.name, NULL, NULL, 'TABLE',
 6      decode(bitand(t.trigflag, 67108864) + bitand(t.trigflag, 134217728),
 7             0, NULL, 67108864, 'DATA', 134217728, 'CACHE', 'ALL')
 8    FROM sys.user$ u, sys.obj$ o, sys.tab$ t
 9    WHERE o.owner# = u.user#
 10      and o.obj# = t.obj#
 11      and bitand(t.property, 1) = 0
 12      and o.subname IS NULL
 13      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 14      and bitand(o.flags, 128) = 0
 15 UNION ALL
 16 SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 17      decode(
 18        decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
 19        decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
 20        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 21    FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
 22    WHERE o.owner# = u.user#
 23      and o.obj# = tp.obj#
 24      and tp.bo# = tab.obj#
 25      and bitand(tab.property, 64) = 0
 26      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 27      and bitand(o.flags, 128) = 0
 28 UNION ALL
 29    SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 30      decode(
 31        decode(bitand(tab.trigflag, 67108864) + bitand(tp.flags, 32), 0, 0, 1) +
 32        decode(bitand(tab.trigflag, 134217728) + bitand(tp.flags, 64), 0, 0, 2),
 33        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 34   FROM sys.user$ u, sys.obj$ o, sys.tabpartv$ tp, sys.tab$ tab
 35    WHERE o.owner# = u.user#
 36      and o.obj# = tp.obj#
 37      and tp.bo# = tab.obj#
 38      and bitand(tab.property, 64) = 64
 39      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 40      and bitand(o.flags, 128) = 0
 41 UNION ALL
 42    SELECT u.name, o.name, o.subname, NULL, 'PARTITION',
 43      decode(
 44        decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
 45        decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
 46        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 47    FROM sys.user$ u, sys.obj$ o, sys.tabcompartv$ tcp, sys.tab$ tab
 48    WHERE o.owner# = u.user#
 49      and o.obj# = tcp.obj#
 50      and tcp.bo# = tab.obj#
 51      and o.namespace = 1 and o.remoteowner IS NULL and o.linkname IS NULL
 52      and bitand(o.flags, 128) = 0
 53 UNION ALL
 54    SELECT u.name, po.name, po.subname, so.subname, 'SUBPARTITION',
 55      decode(
 56        decode(bitand(tab.trigflag, 67108864) + bitand(tcp.flags, 32), 0, 0, 1) +
 57        decode(bitand(tab.trigflag, 134217728) + bitand(tcp.flags, 64), 0, 0, 2),
 58        0, NULL, 1, 'DATA', 2, 'CACHE', 'ALL')
 59    FROM sys.user$ u, sys.obj$ po, sys.obj$ so, sys.tabcompartv$ tcp, sys.tabsubpartv$ tsp, sys.tab$ tab
 60    WHERE so.obj# = tsp.obj#
 61      and po.obj# = tcp.obj#
 62      and tcp.obj# = tsp.pobj#
 63      and tcp.bo# = tab.obj#
 64      and u.user# = po.owner#
 65      and bitand(tab.property, 64) = 0
 66      and po.namespace = 1 and po.remoteowner IS NULL and po.linkname IS NULL
 67      and bitand(po.flags, 128) = 0
 68    ;

View created.

SQL> select table_name, partition_name, object_type, stattype_locked
  2 from dba_tab_statistics_lock
 3 where wner = 'TEST'
 4 and table_name = 'T_PART';

TABLE_NAME                     PARTITION_NAME                 OBJECT_TYPE STATT
------------------------------ ------------------------------ ------------ -----
T_PART                                                        TABLE
T_PART                         P1                             PARTITION    ALL
T_PART                         P2                             PARTITION
T_PART                         PMAX                           PARTITION

使用新创建的这个视图,就可以解决锁定分区的统计信息显示问题。

 


posted @ 2012-08-20 13:08 chen11-1| 编辑 收藏

分区表UNUSED列后的EXCHANGE PARTITION操作

碰到一个有意思的问题,如果分区表执行过SET UNUSED操作,那么是否还可以进行分区的EXCHANGE操作。

 

 

一个简单的测试就可以说明这个问题:

SQL> create table t_part_unused
 2 (id number, name varchar2(30), other varchar2(30))
 3 partition by range (id)
 4 (partition p1 values less than (10),
 5 partition pmax values less than (maxvalue));

Table created.

SQL> insert into t_part_unused
 2 select rownum, table_name, 'abc'
 3 from user_tables;

48 rows created.

SQL> commit;

Commit complete.

SQL> alter table t_part_unused set unused (other);

Table altered.

SQL> desc t_part_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> create table t_temp_unused as
 2 select *
 3 from t_part_unused
 4 where 1 = 2;

Table created.

SQL> desc t_temp_unused
 Name                                    Null?   Type
 ---------------------------------------- -------- ------------------------
 ID                                               NUMBER
 NAME                                             VARCHAR2(30)

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION TB


SQL> alter table t_temp_unused add (other varchar2(30));

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;
with table t_temp_unused
          *
ERROR at line 3:
ORA-14096: tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns


SQL> alter table t_temp_unused set unused (other);

Table altered.

SQL> alter table t_part_unused
 2 exchange partition p1
 3 with table t_temp_unused;

Table altered.

很明显执行了SET UNUSED操作后的表,和普通的表是存在区别的,这种区别导致要求进行EXCHANGE的表必须同样执行SET UNUSED操作,否则就无法执行EXCHANGE的操作。

当目标表中不包含SETE UNUSED的列时,EXCHANGE操作会出现ORA-14097的错误,而如果把列添加到目标表,则会报错ORA-14096,必须在目标表同样对列执行SET UNUSED操作,才能通过EXCHANGE之前的检查。

其实这也不难理解,执行SET UNUSED命令后,数据字典虽然发生了改变,但是表上的数据并没有删除,而EXCHANGE操作只是将两个段的数据字典进行互换,因此如果目标表上缺少SET UNUSED列,是无法执行EXCHANGE操作的。

解决问题的方法有两个,第一个就是例子中展示的可以在目标表上建立列然后同样的执行SET UNUSED操作;另外的一个方法就是对于SET UNUSED列执行DROP COLUMN操作,彻底删除该列。

 


posted @ 2012-08-17 14:56 chen11-1| 编辑 收藏

小议ROWNUM

如何使用ROWNUM是个老生常谈的问题了,本来没有打算专门强调这个问题,但是最近在看Oracle的官方PL/SQL文档时发现了一个严重的错误,借这个机会还是简单说一下。

 

 

首先来看Oracle文档的描述,在10.2的PL/SQL文档中,Oracle关于PL/SQL中直接使用SELECT的查询描述为:

Selecting At Most One Row: SELECT INTO Statement
If you expect a query to only return one row, you can write a regular SQL SELECT statement with an additional INTO clause specifying the PL/SQL variable to hold the result.
If the query might return more than one row, but you do not care about tb values after the first, you can restrict any result set to a single row by comparing the ROWNUM value. If the query might return no rows at all, use an exception handler to specify any actions to take when no data is found.

这个描述是没有问题的,但是到了11.2中,文档的描述变成了:

Single-Row Result Sets
If you expect the query to return only one row, then use the SELECT INTO statement to store values from that row in either one or more scalar variables (see "Assigning Values to Variables with the SELECT INTO Statement") or one record variable (see "SELECT INTO Statement for Assigning Row to Record Variable").
If the query might return multiple rows, but you care about only the nth row, then restrict the result set to that row with the clause WHERE ROWNUM=n. For more information about the ROWNUM pseudocolumn, see Oracle Database SQL Language Reference.

第一个反应是不是我看错了,居然可以通过WHERE ROWNUM = N来限制只返回第N条记录 ,再仔细看了一遍,并和10g的文档对比,发现11.2和10.2中的不同。于是第二个反应是Oracle在11.2中提供了新特性,使得PL/SQL语句中直接SELECT可以通过WHERE ROWNUM来直接控制游标,于是特意在11.2上进行了测试,发现结果和10.2上没有区别,ROWNUM = N是行不通的,除非N等于1。

SQL> select * from v$version;

BANNER
------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
PL/SQL Release 11.2.0.1.0 - Production
CORE   11.2.0.1.0     Production
TNS for 32-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

SQL> select * from tab;

TNAME                         TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES                     TABLE
MLOG$_T_F                     TABLE
MLOG$_T_P                     TABLE
MV_T_ORACLE                   TABLE
SERVICES                      TABLE
SERVICE_RATES                 TABLE
SERVICE_USAGE                 TABLE
SERVICE_USERS                 TABLE
T                             TABLE
T_DEFER                       TABLE
T_F                           TABLE
T_LOAD_LOB                    TABLE
T_P                           TABLE
T_PART                        TABLE

已选择14行。

SQL> set serverout on
SQL> declare
 2 v_name varchar2(30);
 3 begin
 4 select tname into v_name from tab where rownum = 5;
 5 dbms_output.put_line(v_name);
 6 exception
 7 when no_data_found then
 8 dbms_output.put_line('rownum equal the num bigger than 1 is incorrect!');
 9 end;
 10 /
rownum equal the num bigger than 1 is incorrect!

PL/SQL过程已成功完成。

显然Oracle文档这里出现了严重的错误,如果要使用ROWNUM来控制返回第几行结果,那么至少需要2层嵌套查询才可以。

最后简单总结一下ROWNUM,很多人都知道ROWNUM只适用于小于或小于等于,如果进行等于判断,那么只能等于1,不能进行大于的比较。但是却并不了解造成这种限制条件的机制是什么。

其实ROWNUM的返回很简单,ROWNUM总是从1开始,不管当前的记录是否满足查询结果,ROWNUM返回的值都是1,如果这条记录的值最终满足所有的条件,那么ROWNUM会递加,下一条记录的ROWNUM会返回2,否则下一条记录的ROWNUM仍然返回1。

理解了这一点,就清楚为什么一般的ROWNUM大于某个值或等于某个不为1的值是无法返回结果的,因此对于每条记录的ROWNUM都是1,而ROWNUM为1不满足查询的结果,所以下一条记录的ROWNUM不会递增,仍然是1,因此所有的记录都不满足条件。

了解了原理,就可以很容易的写出ROWNUM大于某值的例子:

SQL> select * from tab where rownum = 1 or rownum > 1;

TNAME                         TABTYPE CLUSTERID
------------------------------ ------- ----------
COMPANIES                     TABLE
MLOG$_T_F                     TABLE
MLOG$_T_P                     TABLE
MV_T_ORACLE                   TABLE
SERVICES                      TABLE
SERVICE_RATES                 TABLE
SERVICE_USAGE                 TABLE
SERVICE_USERS                 TABLE
T                             TABLE
T_DEFER                       TABLE
T_F                           TABLE
T_LOAD_LOB                    TABLE
T_P                           TABLE
T_PART                        TABLE

posted @ 2012-08-17 14:53 chen11-1| 编辑 收藏

物化视图刷新出现临时空间不足的问题

朋友解决一个物化视图刷新时碰到的问题。

 

 

数据库版本为10.2.0.4,一次本地聚集物化视图的快速刷新执行了3个小时后出现了临时表空间不足的错误:

ORA-12008: error in materialized view refresh path
ORA-01652: unable to extend temp segment by 32 in tablespace TEMP01

这个物化视图以前的刷新是正常的,只是最近偶尔会出现这个错误。上次出现这个错误,tb通过添加临时数据文件并重启数据库解决了问题。目前数据库的临时表空间已经超过1T的大小,如果不找到问题的原因,仅靠通过添加临时文件显然是不现实的。

通过跟踪刷新物化视图的会话,发现问题确实出在物化视图的快速刷新操作上,而会话的等待事件主要集中在临时表空间的写操作上:direct path write temp。

做了一个awrsql报告,检查了SQL语句和执行计划。由于这个SQL本身相对比较复杂,这就使得快速刷新的MERGE语句更加复杂,简单格式化后,语句长度超过300行,这里就不列出来了。这个SQL的执行计划为:

Execution Plan
Id  Operation  Name  Rows  Bytes  TempSpc Cost (%CPU) Time
0  MERGE STATEMENT      129K(100) 
1  MERGE  MV_NW_KHXX_YDKH_ALL      
2  VIEW       
3  NESTED LOOPS OUTER   4  1592   129K (1) 00:38:46
4  VIEW   4  1084   129K (1) 00:38:46
5  TEMP TABLE TRANSFORMATION       
6  LOAD AS SELECT       
7  VIEW   33  3300   123 (1) 00:00:03
8  WINDOW SORT   33  6600   123 (1) 00:00:03
9  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_2  33  6600   122 (0) 00:00:03
10  LOAD AS SELECT       
11  VIEW   19151  2244K  3533 (1) 00:01:04
12  WINDOW SORT   19151  4114K 9376K 3533 (1) 00:01:04
13  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_1  19151  4114K  3169 (1) 00:00:58
14  SORT GROUP BY   4  1040   125K (1) 00:37:40
15  VIEW   4  1040   125K (1) 00:37:40
16  UNION-ALL       
17  HASH JOIN   1  289   21023 (1) 00:06:19
18  HASH JOIN   1  220   20984 (1) 00:06:18
19  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
20  VIEW   33  1617   2 (0) 00:00:01
21  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
22  VIEW   19151  1290K  38 (0) 00:00:01
23  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
24  HASH JOIN ANTI   1  281   41338 (1) 00:12:25
25  HASH JOIN   1  267   41300 (1) 00:12:24
26  HASH JOIN   1  220   20984 (1) 00:06:18
27  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
28  VIEW   33  1617   2 (0) 00:00:01
29  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
30  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
31  VIEW   19151  261K  38 (0) 00:00:01
32  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
33  HASH JOIN ANTI   1  284   21437 (1) 00:06:26
34  HASH JOIN   1  270   21435 (1) 00:06:26
35  HASH JOIN   1  240   21020 (1) 00:06:19
36  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
37  VIEW   19151  1290K  38 (0) 00:00:01
38  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
39  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
40  VIEW   33  462   2 (0) 00:00:01
41  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
42  HASH JOIN ANTI   1  276   41753 (1) 00:12:32
43  HASH JOIN ANTI   1  262   41714 (1) 00:12:31
44  HASH JOIN   1  248   41711 (1) 00:12:31
45  HASH JOIN   1  201   21396 (1) 00:06:26
46  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  1  171   20982 (1) 00:06:18
47  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
48  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
49  VIEW   33  462   2 (0) 00:00:01
50  TABLE ACCESS FULL  SYS_TEMP_0FD9D6744_E9EB0662  33  2013   2 (0) 00:00:01
51  VIEW   19151  261K  38 (0) 00:00:01
52  TABLE ACCESS FULL  SYS_TEMP_0FD9D6745_E9EB0662  19151  1514K  38 (0) 00:00:01
53  MAT_VIEW ACCESS BY INDEX ROWID MV_NW_KHXX_YDKH_ALL  1  127   2 (0) 00:00:01
54  INDEX UNIQUE SCAN  I_SNAP$_MV_NW_KHXX_YDKH_AL  1    1 (0) 00:00:01
 

从执行计划上看出一些疑问,MLOG$_DW_YH_JBXX表的结果只有1行,而实际上这张表的大小超过100W。

在默认情况下,收集SCHEMA的统计信息是不会收集物化视图日志的,而且即使Oracle收集统计信息时可以收集物化视图日志的统计信息,对于当前的情况,也无济于事。因为当前刷新的物化视图是第一个嵌套物化视图,它建立在其他两个物化视图的基础上,也就是说,只有刷新了其他两个物化视图之后,对应的物化视图日志中才会有记录,而其他时候,物化视图日志中的记录都是0。

其实现在问题已经确定了,由于物化视图日志没有统计信息,Oracle认为物化视图日志中记录很少,产生了一个最外层为NESTED LOOP的执行计划,导致刷新效率十分低下。对于这种情况,其实可以通过一次完全刷新来解决问题,但是对于当前的情况,仍然是不可行的。因为这个物化视图是数据仓库系统中的一个中间结果表,下游还有很多物化视图以及其他系统依赖于当前物化视图的增量数据。一旦这个物化视图执行了完全刷新,就会导致所有依赖当前对象的下游物化视图的增量刷新变成了完全刷新。

当前快速刷新碰到的问题其实就是Oracle的默认策略认为物化视图日志中的数据量应该远小于基表的数据量,这样快速刷新才会有性能上的优势,但是当前情况下,物化视图日志的数据量和基表的数据量处于同一个数量级,因此缺少统计信息后,快速刷新的执行计划变得十分的低效。而如果采用完全刷新来解决当前物化视图的问题,那么实际上是把这个问题扩大到下游所有有依赖关系的物化视图上。

为了解决这个问题,首先想到的是optimizer_dynamic_samping参数,通过设置会话级的参数,控制物化视图日志刷新之前,进行详细的动态统计信息采样,使之可以得到一个适合的执行计划。

但是将optimizer_dynamic_samping设置为10后,发现对MLOG$_DW_YH_JBXX表不起任何作用,刷新的执行计划中,MLOG$_DW_YH_JBXX表的行数仍然为1。

看来没有别的办法,只有手工显示的对物化视图日志表执行统计信息的收集工作,当统计信息收集完成后,再次运行物化视图的快速刷新,结果用了不到10分钟的时间,物化视图就刷新成功了。

这次执行计划变为:

Execution Plan

Id  Operation  Name  Rows  Bytes  TempSpc Cost (%CPU) Time
0  MERGE STATEMENT      141K(100) 
1  MERGE  MV_NW_KHXX_YDKH_ALL      
2  VIEW       
3  HASH JOIN OUTER   16997  6606K 4704K 141K (1) 00:42:19
4  VIEW   16997  4498K  129K (1) 00:38:50
5  TEMP TABLE TRANSFORMATION       
6  LOAD AS SELECT       
7  VIEW   33  3300   123 (1) 00:00:03
8  WINDOW SORT   33  6600   123 (1) 00:00:03
9  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_2  33  6600   122 (0) 00:00:03
10  LOAD AS SELECT       
11  VIEW   19151  2244K  3533 (1) 00:01:04
12  WINDOW SORT   19151  4114K 9376K 3533 (1) 00:01:04
13  TABLE ACCESS FULL  MLOG$_MV_NW_KHXX_YDKH_1  19151  4114K  3169 (1) 00:00:58
14  SORT GROUP BY   16997  4315K  125K (1) 00:37:44
15  VIEW   16997  4315K  125K (1) 00:37:44
16  UNION-ALL       
17  HASH JOIN   267  50196   21078 (1) 00:06:20
18  HASH JOIN   191  26549   21076 (1) 00:06:20
19  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
20  VIEW   19151  1290K  38 (0) 00:00:01
21  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
22  VIEW   33  1617   2 (0) 00:00:01
23  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
24  HASH JOIN   16188  2845K  41394 (1) 00:12:26
25  VIEW   33  1617   2 (0) 00:00:01
26  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
27  HASH JOIN RIGHT ANTI   11594  1483K  41391 (1) 00:12:26
28  VIEW   19151  261K  38 (0) 00:00:01
29  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
30  HASH JOIN   11594  1324K  41352 (1) 00:12:25
31  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
32  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
33  HASH JOIN ANTI   9  1647   21492 (1) 00:06:27
34  HASH JOIN   9  1521   21490 (1) 00:06:27
35  HASH JOIN   191  26549   21076 (1) 00:06:20
36  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
37  VIEW   19151  1290K  38 (0) 00:00:01
38  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
39  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
40  VIEW   33  462   2 (0) 00:00:01
41  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
42  HASH JOIN ANTI   533  93275   41808 (1) 00:12:33
43  HASH JOIN RIGHT ANTI   533  85813   41769 (1) 00:12:32
44  VIEW   33  462   2 (0) 00:00:01
45  TABLE ACCESS FULL  SYS_TEMP_0FD9D674C_E9EB0662  33  2013   2 (0) 00:00:01
46  HASH JOIN   533  78351   41766 (1) 00:12:32
47  HASH JOIN   11594  1324K  41352 (1) 00:12:25
48  TABLE ACCESS FULL  MLOG$_DW_YH_JBXX  11859  810K  21037 (1) 00:06:19
49  MAT_VIEW ACCESS FULL MV_NW_KHXX_YDKH_1  9008K 403M  20279 (1) 00:06:06
50  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_2  283K 8293K  413 (2) 00:00:08
51  VIEW   19151  261K  38 (0) 00:00:01
52  TABLE ACCESS FULL  SYS_TEMP_0FD9D674D_E9EB0662  19151  1514K  38 (0) 00:00:01
53  MAT_VIEW ACCESS FULL  MV_NW_KHXX_YDKH_ALL  1701K 206M  3888 (2) 00:01:10

可以看到,最外层的执行计划已经变成了HASH JOIN OUTER,而且虽然物化视图日志MLOG$_DW_YH_JBXX的统计信息仍然少了2个数量级,但是比原本的1条记录要靠谱多了。

至此,问题得以解决,不过为了避免这种情况的再次发生,最好的办法是将物化视图日志的统计信息收集工作放到物化视图刷新之前进行,这样可以确保物化视图的快速刷新可以得到最精确的统计信息,从而得到最优的执行计划。

posted @ 2012-08-17 09:36 chen11-1| 编辑 收藏

提高代码可读性的十大注释技巧

很多程序员在写代码的时候往往都不注意代码的可读性,让别人在阅读代码时花费更多的时间。其实,只要程序员在写代码的时候,注意为代码加注释,并以合理的格式为代码加注释,这样就方便别人查看代码,也方便自己以后查看了。下面分享十个加注释的技巧:

1. 逐层注释

为每个代码块添加注释,并在每一层使用统一的注释方法和风格。例如:

针对每个类:包括摘要信息、作者信息、以及最近修改日期等;

针对每个方法:包括用途、功能、参数和返回值等。

在团队工作中,采用标准化的注释尤为重要。当然,使用注释规范和工具(例如C#里的XML,Java里的Javadoc)可以tb更好的推动注释工作完成得更好。

2. 使用分段注释

如果有多个代码块,而每个代码块完成一个单一任务,则在每个代码块前添加一个注释来向读者说明这段代码的功能。例子如下:

// Check that all data records
// are correct
foreach (Record record in records)
...{
if (rec.checkStatus()==Status.OK)
...{
. . .
}
}
// Now we begin to perform
// transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .

3. 在代码行后添加注释

如果多行代码的每行都要添加注释,则在每行代码后添加该行的注释,这将很容易理解。例如:

const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F;    // mask bit TCP
在分隔代码和注释时,有的开发者使用tab键,而另一些则使用空格键。然而由于tab键在各编辑器和IDE工具之间的表现不一致,因此最好的方法还是使用空格键。

4. 不要侮辱读者的智慧

避免以下显而易见的注释:写这些无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。

if (a == 5)      // if a equals 5
counter = 0; // set the counter to zero

5. 礼貌点

避免粗鲁的注释,如:“注意,愚蠢的使用者才会输入一个负数”或“刚修复的这个问题出于最初的无能开发者之手”。这样的注释能够反映到它的作者是多么的拙劣,你也永远不知道谁将会阅读这些注释,可能是:你的老板,客户,或者是你刚才侮辱过的无能开发者。

6. 关注要点

不要写过多的需要转意且不易理解的注释。避免ASCII艺术,搞笑,诗情画意,hyperverbosity的注释。简而言之,保持注释简单直接。

7. 使用一致的注释风格

一些人坚信注释应该写到能被非编程者理解的程度。而其他的人则认为注释只要能被开发人员理解就行了。无论如何,Successful Strategies for Commenting Code已经规定和阐述了注释的一致性和针对的读者。就个人而言,我怀疑大部分非编程人员将会去阅读代码,因此注释应该是针对其他的开发者而言。

8. 使用特有的标签

在一个团队工作中工作时,为了便于与其它程序员沟通,应该采用一致的标签集进行注释。例如,在很多团队中用TODO标签表示该代码段还需要额外的工作。

int Estimate(int x, int y)
...{
// TODO: implement the calculations
return 0;
}
注释标签切忌不要用于解释代码,它只是引起注意或传递信息。如果你使用这个技巧,记得追踪并确认这些信息所表示的是什么。

9. 在代码时添加注释

在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。如果在代码最后再添加同样注释,它将多花费你一倍的时间。而“我没有时间写注释”,“我很忙”和“项目已经延期了”这都是不愿写注释而找的借口。一些开发者觉得应该write comments before code,用于理清头绪。例如:

public void ProcessOrder()
...{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}

10. 为自己注释代码

当注释代码时,要考虑到不仅将来维护你代码的开发人员要看,而且你自己也可能要看。用Phil Haack大师的话来说就是:“一旦一行代码显示屏幕上,你也就成了这段代码的维护者”。因此,对于我们写得好(差)的注释而言,我们将是第一个受益者(受害者)。

posted @ 2012-08-16 14:50 chen11-1 阅读(1753) | 评论 (2)编辑 收藏

PHP-FPM高负载的解决办法

这里只是介绍了php-fpm的优化方法的,但一般情况下和nginx组合使用的时候,单独优化其中一项的话,作用不是特别的大,同时还需要对nginx进行优化.nginx的做法方法参考:http://blog.haohtml.com/archives/6213.上面的优化前和优化后的图,看得出前后差距还是特别的大的.

导致nginx 502 bad gateway的PHP-CGI(FASTCGI)

NGINX频爆502 BAD GATEWAY的错误,看了网上的教程,仍没有彻底解决。

目前我总结的解决502 BAD GATEWAY的方式有:

1.视服务器的性能,在php-fmp.conf里增加max_children的值,我目前用的15.

2.用reload参数定时重载php-fpm。这个主要原因是php脚本执行时间过长造成的,重载php-fpm能杜绝这个问题。如何彻底解决php-cgi脚本占用大量内存从而导致502错误的产生还值得进一步探讨,目前该做法不失为一种好办法。

具体的做法是,用crontab让php-fpm平滑重启,从而不影响PHP脚本的tb运行。

*/10 * * * * /usr/local/php/sbin/php-fpm reload

=================== 优化设置 =========================


When you running a highload website with PHP-FPM via FastCGI, the following tips may be useful to you : )

如果您高负载网站使用PHP-FPM管理FastCGI,这些技巧也许对您有用:)

1. Compile PHP’s modules as less as possible, the simple the best (fast);

1.尽量少安装PHP模块,最简单是最好(快)的

2. Increas PHP FastCGI child number to 100 and even more. Sometime, 200 is OK! ( On 4GB memory server);

2.把您的PHP FastCGI子进程数调到100或以上,在4G内存的服务器上200就可以

注:我的1g测试机,开64个是最好的,建议使用压力测试获取最佳值

3. Using SOCKET PHP FastCGI, and put into /dev/shm on Linux;

3.使用socket连接FastCGI,linux操作系统可以放在 /dev/shm中

注:在php-fpm.cnf里设置<value name=”listen_address”>/tmp/nginx.socket</value>就可以通过socket连接FastCGI了,/dev/shm是内存文件系统,放在内存中肯定会快了.记得这时也要在nginx里的配置里进行修改,保持一致.

location ~ .*\.(php|php5)?$
{
#将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快。
fastcgi_pass  unix:/tmp/php-cgi.sock;
#fastcgi_pass  127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}

4. Increase Linux “max open files”, using the following command (must be root):

# echo ‘ulimit -HSn 65536′ >> /etc/profile
 # echo ‘ulimit -HSn 65536 >> /etc/rc.local
 # source /etc/profile 


4.调高linux内核打开文件数量,可以使用这些命令(必须是root帐号)

echo ‘ulimit -HSn 65536′ >> /etc/profile
 echo ‘ulimit -HSn 65536′ >> /etc/rc.local
 source /etc/profile 


注:我是修改/etc/rc.local,加入ulimit -SHn 51200的

5. Increase PHP-FPM open file description rlimit:

# vi /path/to/php-fpm.conf
 Find “<value name=”rlimit_files”>1024</value>”
 Change 1024 to 4096 or higher number.
 Restart PHP-FPM.


5. 增加 PHP-FPM 打开文件描述符的限制:

# vi /path/to/php-fpm.conf
 找到“<value name=”rlimit_files”>1024</value>”
 把1024 更改为 4096 或者更高.
重启 PHP-FPM.


6. Using PHP code accelerator, e.g eAccelerator, XCache. And set “cache_dir” to /dev/shm on Linux.
6.使用php代码加速器,例如 eAccelerator, XCache.在linux平台上可以把`cache_dir`指向 /dev/shm

至于其它的优化见李宴的bltbog一篇文章:http://blog.s135.com/post/375/

This entry was posted in js框架 and tagged nginx, php-fpm by admin. Bookmark the permalink.

One thought on “PHP-FPM高负载的解决办法

posted @ 2012-08-16 14:48 chen11-1| 编辑 收藏

php下用iconv函数转换字符编码的问题

昨天在调试 WAP 网站时发现,在增加了 GB2312 到 UTF-8 转化以后,有些页面显示不正常了——有些页面只有一半的内容,另一半被截掉了。因为被截掉的部分包含了<p>的后半个标签</p>,因此整个页面都显示不出来,而报告错误。经过猜测、尝试,最后终于把问题集中在了 iconv 函数上。在经过高人指点以后,发现这个函数的第二个参数,除了可以指定要转化到的编码以外,还可以增加两个后缀://TRANSLIT 和 //IGNORE,其中 //TRANSLIT 会自动将不能直接转化的字符变成一个或多个近似的字符,//IGNORE 会忽略掉不能转化的字符,而默认效果是从第一个非法字符截断。但是我尝试了//TRANSLIT 和 //IGNORE 这两个后缀,效果还是不对。于是我想问题可能不是出在这里。

从 GB2312 到 UTF-8 转化应该不会有不能转化的字符,因为 UTF-8 的字符集完全包含了 GB2312 中的字符,所以我tb想大概是前面要转化的字符集指定错了,于是我尝试着把 GB2312 改成 GBK

$ary=addslashes(iconv("GB2312", "UTF-8", $ary));

问题解决!虽然那两个后缀在这里没派上用场,不过也算学了一招,以后肯定会用到的。补记:改成 GBK 后,发现仍然有一封邮件的内容解析不正确。在另一位高人指点下,先换成 GB18030,问题依旧,然后改用 mb_convert_encoding 进行转换,问题解决!不知道是 mb_convert_encoding 问题,还是我的系统问题,我用 mb_convert_encoding 时不支持 GB18030 编码。另外,用 GBK 或者GB18030 作为输入编码,并在输出编码中加上 //IGNORE 后缀,用 iconv 函数也能解决那封含有错误编码的邮件内容解析不正确的问题。不过用 mb_convert_encoding 可以指定多种输入编码,它会根据内容自动识别,这个比 iconv 要好的多。 这里可以将iconv改成从gbk到utf8的转换,不使用gb2312.

$ary=addslashes(iconv("GBK", "UTF-8", $ary));

其实,同事在生成图片文字水印的时候也遇到了这种问题,同事最初用的是GB2312字符集,结果直接报错,说是字符串的offset有问题,但仔细检查后却没有这种问题。后来才发现是直接调用的这个iconv转换出错了。
原来的转换是从gb2312往 UTF8转换,表面上确实没有什么问题,然而,现在的人特别爱装酷,受影响的那位同志,用的是繁体字,繁体字的字库大多情况是属于GBK的,所以后来换成GBK后就正常了。
估计以后再遇上用火星文的朋友,就真的只能使用andot提出的这种方法了。转换成18030,再使用ignore参数。哈哈

mbstring好象最初的版本里没有使用,如果换成这个,估计代码工作量非常大,先将就着点了

posted @ 2012-08-16 14:45 chen11-1| 编辑 收藏

忘记 VSS Admin 密码 ! .

一不小心将VSS 6 admin用户的密码忘记(再此证明我的粗心),Google了一番,找到以下信息

the secret is to hack the um.dat file to remove the Admin password

from offset 80 the bytes are (all numbers are hex)

0:80  55 55 bc 7f 41 64 6d 69 6e 00 00 00 00 00 00 00
0:90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0:a0  00 00 00 00 90 6e 00 00 a8 01 00 00 00 00 00 00

Just load the um.dat file into a hex editor and change the bytes from =
offset 80 to exactly what is shown above. When the SourceSafe admin tool =
starts it believes that the admin password has never been set.

the hex values above are taken from a 'virgin' um.dat file

of course, you didn't get this from me....and ALWAYS backup first (just =
in case I'm wrong)

有朋友看不懂,我用中文解释一下

如果忘记了密码,打开你vss数据库所在的文件夹,打开data目录,
找到um.dat文件,用hex编辑器打开编辑它,从offset 80的55 55 开始
将值改为如上文所述的样子,然后保存,这样um.dat文件就回到了
状态(virgin ?  :)),然后打开vss admin,用admin用户登录,tb不需要
密码了。

posted @ 2012-08-16 10:48 chen11-1| 编辑 收藏

jquery.validate remote 和 自定义验证方法

$(function(){

var validator = $("#enterRegForm").validate({
debug:false, //调试模式取消submit的默认提交功能
//errorClass: "error",//默认为错误的样式类为:error
//validClass: "check",//验证成功后的样式,默认字符串valid
focusInvalid: true,//表单提交时,焦点会指向第一个没有通过验证的域
//focusCleanup:true;//焦点指向错误域时,隐藏错误信息,不可与focusInvalid一起使用!
onkeyup: true,
errorElement: "div",
submitHandler: function(form){ //表单提交句柄,为一回调函数,带一个参数:form
form.submit(); //提交表单
},

rules: {
"enterprise.enName": {
required: true,
minlength: 6,
remote:{
url: "/nameServlet",     //后台处理程序
type: "get",               //数据发送方式
dataType: "json",           //接受数据格式
data: {                     //要传递的数据
enName: function() {
return $("#enName").val();
}
}

}
},

"user.passWord":{
required:true,
rangelength:[6,18]
},
passWordConf:{
required:true,
rangelength:[6,18],
equalTo:"#passWord" //此处的passWord 是<input id="passWord"> 一开始还以为是name的值呢,气死了
}

},

messages: { //自定义验证消息
"enterprise.enName": {
required: "请填写企业名称!",
minlength: $.format("至少要{0}个字符!"),
remote:$.format("该企业名称已存在!")

},
"user.passWord":{
required:"请填写确认密码!",
rangelength:$.format("密码要在{0}-{1}个字符之间!")
},
passWordConf:{
required:"请填写确认密码!",
rangelength:$.format("确认密码要在{0}-{1}个字符之间!"),
equalTo:"确认密码要和密码一致!"
},

errorPlacement: function(error, element) { //验证消息放置的地方
//error.appendTo( element.parent("td").next("td").children(".msg") );
error.appendTo( element.parent(".field").next("div"));
},
highlight: function(element, errorClass) { //针对验证的表单设置高亮
$(element).addClass(errorClass);
},
success: function(div) {
div.addClass("valid");
}
});

});

自定义方法;

新建一个js文件:$(document).ready(function(){
// 字符最小长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMinLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length >= param);
}, $.validator.format("长度不能小于{0}!"));

// 字符最大长度验证(一个中文字符长度为2)
jQuery.validator.addMethod("stringMaxLength", function(value, element, param) {
var length = value.length;
for ( var i = 0; i < value.length; i++) {
if (value.charCodeAt(i) > 127) {
length++;
}
}
return this.optional(element) || (length <= param);
}, $.validator.format("长度不能大于{0}!"));

// 字符验证
jQuery.validator.addMethod("stringCheck", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "只能包括中文字、英文字母、数字和下划线");

// 中文字两个字节
jQuery.validator.addMethod("byteRangeLength", function(value, element, param) {
var length = value.length;
for(var i = 0; i < value.length; i++){
if(value.charCodeAt(i) > 127){
length++;
}
}
return this.optional(element) || ( length >= param[0] && length <= param[1] );
}, "请确保输入的值在3-15个字节之间(一个中文字算2个字节)");

// 字符验证
jQuery.validator.addMethod("string", function(value, element) {
return this.optional(element) || /^[\u0391-\uFFE5\w]+$/.test(value);
}, "不允许包含特殊符号!");
// 必须以特定字符串开头验证
jQuery.validator.addMethod("begin", function(value, element, param) {
var begin = new RegExp("^" + param);
return this.optional(element) || (begin.test(value));
}, $.validator.format("必须以 {0} 开头!"));
// 验证两次输入值是否不相同
jQuery.validator.addMethod("notEqualTo", function(value, element, param) {
return value != $(param).val();
}, $.validator.format("两次输入不能相同!"));
// 验证值不允许与特定值等于
jQuery.validator.addMethod("notEqual", function(value, element, param) {
return value != param;
}, $.validator.format("输入值不允许为{0}!"));

// 验证值必须大于特定值(不能等于)
jQuery.validator.addMethod("gt", function(value, element, param) {
return value > param;
}, $.validator.format("输入值必须大于{0}!"));

// 验证值小数位数不能超过两位
jQuery.validator.addMethod("decimal", function(value, element) {
var decimal = /^-?\d+(\.\d{1,2})?$/;
return this.optional(element) || (decimal.test(value));
}, $.validator.format("小数位数不能超过两位!"));
//字母数字
jQuery.validator.addMethod("alnum", function(value, element) {
return this.optional(element) || /^[a-zA-Z0-9]+$/.test(value);
}, "只能包括英文字母和数字");
// 汉字
jQuery.validator.addMethod("chcharacter", function(value, element) {
var tel = /^[\u4e00-\u9fa5]+$/;
return this.optional(element) || (tel.test(value));
}, "请输入汉字");
// 身份证号码验证
jQuery.validator.addMethod("isIdCardNo", function(value, element) {
return this.optional(element) || /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/.test(value)||/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[A-Z])$/.test(value);
}, "请正确输入您的身份证号码");

// 手机号码验证
jQuery.validator.addMethod("isMobile", function(value, element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
return this.optional(element) || (length == 11 && mobile.test(value));
}, "请tb正确填写您的手机号码");

// 电话号码验证
jQuery.validator.addMethod("isTel", function(value, element) {
var tel = /^\d{3,4}-?\d{7,9}$/;    //电话号码格式010-12345678
return this.optional(element) || (tel.test(value));
}, "请正确填写您的电话号码");

// 联系电话(手机/电话皆可)验证
jQuery.validator.addMethod("isPhone", function(value,element) {
var length = value.length;
var mobile = /^(((13[0-9]{1})|(15[0-9]{1}))+\d{8})$/;
var tel = /^\d{3,4}-?\d{7,9}$/;
return this.optional(element) || (tel.test(value) || mobile.test(value));

}, "请正确填写您的联系电话");

// 邮政编码验证
jQuery.validator.addMethod("isZipCode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码");

});

该文件要先被引用再对form进行验证。

注意:remote 中的url 为servlet,但是这个servlet该怎么写呢

一开始以为是跟平时的一样:out.println("用户名"+name+"已存在!");

虽然可以验证,但是还是存在问题:输入个不存在的name还是提示存在。

后来还是Google,看到别人也是犯了这样的错误,解决方法时返回true,false.

但是 doGet 是void类型的,return true 是行不通的。

后来又请教群里的高手,正确写法是out.println("true");就可以了。

哎,费了老长时间

总算是把问题给解决了。

posted @ 2012-08-15 15:02 chen11-1| 编辑 收藏

jQuery mouseover mouseout事件在IE下闪烁的解决方法

$("#category ul").find("li").each(
    function() {
        $(this).mouseover(
            function() {
                $("#" + this.id + "_menu").show();
                $(this).addClass("a");
            }
        );
        $(this).mouseout(
            function() {
                $(this).removeClass("a");
                $("#" + this.id + "_menu").hide();
            }
        );
    }
);

浏览器之间的不兼容一直令前端开发者的头疼,而 IE 更是噩梦。鼠标在下拉菜单移动时菜单会不断闪烁,tb说明不断触发了 mouseover 和 mouseout 事件。

这貌似涉及到所谓的“事件冒泡”,我不懂 JavaScript,就不在误人子弟了,详情请自己 Google,这里只给出解决方法:将 mouseover 改成 mouseentermouseout 改成 mouseleave

$("#category ul").find("li").each(
    function() {
        $(this).mouseenter(
            function() {
                $("#" + this.id + "_menu").show();
                $(this).addClass("a");
            }
        );
        $(this).mouseleave(
            function() {
                $(this).removeClass("a");
                $("#" + this.id + "_menu").hide();
            }
        );
    }
);

最后指出一点,mouseenter 和 mouseleave 事件是 jQuery 库中实现的,似乎(再次声明我不懂 JavaScript,所以这里用“似乎”)并不是浏览器的原生事件,所以如果你想自己写 JavaScript 实现的话,请自行 Google,或者查看 jQuery 源码,如果你能看懂的话。

参考链接:JQuery HowTo: Problems with jQuery mouseover / mouseout events

转自:http://demon.tw/programming/jquery-mouseover-mouseout.html

posted @ 2012-08-15 15:01 chen11-1| 编辑 收藏

jQuery Datepicker 中文

以前在使用 js 日历时,没有使用过 jQuery Datepicker,今天第一次使用发现非常的好用。使用时需要将日历文字显示为中文,打开前边的链接在文章底部就可以看到将 jQuery Datepicker 文字显示为中文的方法,在http://jquery-ui.googlecode.com/svn/trunk/ui/i18n/可以看到各种版本的语言,中文文件内容如下:

jQuery(function($){
        $.datepicker.regional['zh-CN'] = {
                closeText: '关闭',
                prevText: '<上月',
                nextText: '下月>',
                currentText: '今天',
                monthNames: ['一月','二月','三月','四月','五月','六月',
                '七月','八月','九月','十月','十一月','十二月'],
                monthNamesShort: ['一','二','三','四','五','六',
                '七','八','九','十','十一','十二'],
                dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
                dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
                dayNamesMin: ['日','一','二','三','四','五','六'],
                weekHeader: '周',
                dateFormat: 'yy-mm-dd',
                firstDay: 1,
                isRTL: false,
                showMonthAfterYear: true,
                yearSuffix: '年'};
        $.datepicker.setDefaults($.datepicker.regional['zh-CN']);
});

使用时引入该文件, jQuery Datepicker 就会显示简体中文,但为了tb网站性能,最好将它整合到一个 js 文件中。

$(document).ready(function() {
    $("#sdate,#edate").datepicker();
});

使用时也可以添加其他的选项,如下所示,具体的可以参考 jQuery Datepicker 官方文档。

$(document).ready(function () {
    $("#sdate,#edate").datepicker({
        numberOfMonths: 2,  //显示两个月
        minDate: 0  //从当前日期起可选
    });
});

本篇简单介绍了下 jQuery UI Datepicker 显示中文的方法,希望能对遇到这个问题又不太熟悉英文的朋友带来一点帮助

posted @ 2012-08-15 15:00 chen11-1| 编辑 收藏

动态添加input表单元素的js代码

addinput.js

//======================
//功能:在表单中input file控件
//参数:parentID---要插入input file控件的父元素ID
// inputID----input file控件的ID,这个一定要和name保持一致的,否则会出问题
// maxNum --- 最大数量 0为不限制
//======================
function createInput(parentID,inputFileID, maxNum){

if (maxNum > 0) {
x=document.getElementsByName(inputFileID);
y=x.length;
if (y >= maxNum) {
alert('最多只允许添加' + maxNum + '个');
return false;
}
}

var parent=$G(parentID);//获取父元素

var div=document.createElement("div");//创建一个div容器tb用于包含input file
var x=parseInt(Math.random()*(80-1))+1;
var divName=inputFileID+x.toString();//随机div容器的名称
div.name=divName;
div.id=divName;

var aElement=document.createElement("input"); //创建input
aElement.name=inputFileID;
aElement.id=inputFileID;
aElement.type="text";//设置类型为file
aElement.className = "dynInputLen";

var delBtn=document.createElement("input");//再创建一个用于删除input file的Button
delBtn.type="button";
delBtn.value=" ";
delBtn.className = "btn_del";
delBtn.onclick=function(){ removeInput(parentID,divName)};//为button设置tbonclick方法

div.appendChild(aElement);//将input file加入div容器
div.appendChild(delBtn);//将删除按钮加入div容器
parent.appendChild(div);//将div容器加入父元素
}
//============================
//功能:删除一个包含input file的div 容器
//参数:parentID---input file控件的父元素ID
// DelDivID----个包含input file的div 容器ID
//============================
function removeInput(parentID,DelDivID){
var parent=$G(parentID);
parent.removeChild($G(DelDivID));
}
//通过元素ID获取文档中的元素
function $G(v){return document.getElementById(v);}

在html里引入addinput.js文件.html代码如下:

<div id="div_zc" class="dynInput">
  <input name="zhuancheng[]" type="text" id="zhuancheng" value="" maxlength="50" />
  </div>
  <div style="clear:both;margin-top:10px; padding-left:5px;">
  <input type="button" onClick="createInput('div_zc','zhuancheng[]', 3)" name="button" id="button" value="+ 添加(限3条)" class="btn_add">

可以设定最多可以添加多少个input,如果设置为0的话,则表示不限制数量。

posted @ 2012-08-15 14:59 chen11-1| 编辑 收藏

关于全局变量的看法

在网上,看到一个问题 “什么是全局变量?”说实话,这个问题我想了一会儿。觉得有二义性,可能大部分人会说,只要是在堆栈外面定义的 就是全局。 我觉得,全局这个概念,我想反问提问者,是线程全局?进程全局? 好吧,我今天想说说后者的。其实,在写hook的时候,大家都知道,进程需要共享dll中的全局变量。没错,就是它,我觉得被进程共享的 才叫全局。

 

Cpp代码  
  1. #pragma comment (linker, "/SECTION:GlobalValue,RWS")   
  2.   
  3. #include <iostream>   
  4.   
  5. #pragma data_seg("GlobalValue")   
  6. int g_iCnt = 1000;   
  7. #pragma data_seg()   
  8.   
  9. int PlusShow()   
  10. {   
  11.    return printf("PlusShow %d",++g_iCnt);   
  12. }   
  13.   
  14. int Show()   
  15. {   
  16.   return printf("Show %d",g_iCnt);   
  17. }   
  18.   
  19. int main(int argc)   
  20. {   
  21.   if(argc < 2)   
  22.   {   
  23.       return Show();   
  24.   }   
  25.      
  26.   PlusShow();   
  27.   getchar();   
  28.   return 0;   
  29. }  


其实 不同段放的 不仅仅是变量。可以是函数 或者对象,非常灵活。看你自己怎么用了:)

posted @ 2012-08-13 15:55 chen11-1| 编辑 收藏

一个全局的css global.css

Css代码 复制代码 收藏代码
  1. @charset "utf-8";   
  2.   
  3. /* =Reset default browser CSS. Based on work by Eric Meyer:http://meyerweb.com/eric/tools/css/reset/index.html   
  4. -------------------------------------------------------------- */   
  5. html, body, div, span, applet, object, iframe,   
  6. h1, h2, h3, h4, h5, h6, p, blockquote, pre,   
  7. a, abbr, acronym, address, big, cite, code,   
  8. del, dfn, em, font, ins, kbd, q, s, samp,   
  9. small, strike, strong, sub, sup, tt, var,   
  10. dl, dt, dd, ol, ul, li,   
  11. fieldset, form, label, legend,   
  12. table, caption, tbody, tfoot, thead, tr, th, td { margin:0; padding:0;}   
  13. :focus { outline:0;}   
  14. ol, ul { list-style:none;}   
  15. h1, h2, h3, h4, h5, h6 { font-size:100%; font-weight:normal;}   
  16. /* tables still need 'cellspacing="0"' in the markup */   
  17. table { border-collapse:separate; border-spacing:0;}   
  18. caption, th, td { font-weight:normal; text-align:left;}   
  19. blockquote:before, blockquote:after,   
  20. q:before, q:after { content:'';}   
  21. blockquote, q { quotes:'' '';}   
  22. a img { border:0;}   
  23. article, aside, details, figcaption, figure,   
  24. footer, header, hgroup, menu, nav, section { display:block;}   
  25.   
  26. /* =Structure   
  27. ----------------------------------------------- */   
  28. body { font:12px/1.5 'Microsoft yahei', Tahoma, Geneva, sans-serif; color:#666; background:#d5d8dd;}   
  29. a { text-decoration:none; color:#4a5566;}   
  30. a:hover { text-decoration:underline; color:#3061b0;}   
  31. .area { width:960px; margin:auto;}   
  32. .pt { padding-top:20px; !important;}   
  33. .mb { margin-bottom:20px !important;}   
  34. .clearfix:after { content:'.'; display:block; height:0; clear:both; visibility:hidden; font-size:0;}   
  35. .clearfix { min-height:1%;}   
  36. .red { color:#c33;}   
  37. .tright { text-align:right;}   
  38. .dis { display:none !importantb;}   
  39.   
  40. /* = Layout   
  41. ----------------------------------------------- */   
  42. /* header */   
  43.   
  44.   
  45. /* body */   
  46.   
  47.   
  48. /* footer */  

posted @ 2012-08-13 15:51 chen11-1| 编辑 收藏

java 时间大小的比较

Java代码  
  1. import java.text.ParseException;   
  2. import java.text.SimpleDateFormat;   
  3. import java.util.Calendar;   
  4. import java.util.Date;   
  5.   
  6. public class A {   
  7.     public static void main(String[] args) throws ParseException{   
  8.         Date d1 = Calendar.getInstance().getTime();   
  9.         String s1 = new SimpleDateFormat("yyyy"+"-"+"MM"+"-"+"dd HH" + ":" + "mm" + ":" + "ss").format(d1);   
  10.         System.out.println("s1==="+stb1);   
  11.            
  12.         String time = "2011-08-13 14:42:43";   
  13.         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");   
  14.         Date dd= sdf.parse(time.toString());   
  15.         String ss = new SimpleDateFormat("yyyy"+"-"+"MM"+"-"+"dd HH" + ":" + "mm" + ":" + "ss").format(dd);   
  16.         System.out.println("ss==="+ss);   
  17.            
  18.         int i = d1.compareTo(dd);   
  19.         if(i<0){   
  20.             System.out.println("d1更早");   
  21.         }else if(i==0){   
  22.             System.out.println("d1一样");   
  23.         }else{   
  24.             System.out.println("dd更早");   
  25.         }   
  26.            
  27.     }   
  28. }  

posted @ 2012-08-13 15:36 chen11-1| 编辑 收藏

采用jdbc批处理 提高jdbc效率 .

1.将jdbc操作改成批处理  addBatch(); //添加批处理

 

2.使用PreparedStatement

 

 

代码:

eg:

 

 

  1. Connection conn = DBUtils.getInstance().getConnetion();  
  2.   
  3. conn.setAutoCommit(false );    
  4.   
  5. PreparedStatement pstmt = null;  
  6.   
  7. try   
  8.   
  9. pstmt = conn.preparedStatement("insert into test1(a,b) vlaues (?,?)");  
  10.   
  11. pstmt.clearBatch();  
  12.   
  13. for(int i = 0; i<100000;i++){  
  14.   
  15.      pstmt.setInt(1,i);  
  16.   
  17.      pstmt.setString(2,"value"+i);  
  18.   
  19.      pstmt.addBatch();  
  20.   
  21.     if(i % 10000){  
  22.   
  23.             pstmt.executbeBatch();  
  24.   
  25.     }  
  26.   
  27.   
  28.   
  29. }  
  30.   
  31.   
  32.   
  33.   
  34.   
  35. pstmt.executeBatch();  
  36.   
  37.   
  38.   
  39. conn.commit();  
  40.   
  41. catch(Exception e) {  
  42.   
  43.       conn.rollback();  
  44.   
  45. }  finally {  
  46.   
  47.      conn.setAutocommit(true);  
  48.   
  49. }  

posted @ 2012-08-10 14:59 chen11-1| 编辑 收藏

JavaScript中Array(数组)的属性和方法 .

数组有四种定义的方式

使用构造函数:
var a = new Array();
var b = new Array(8);
var c = new Array("first", "second", "third");
或者数组直接量:
var d = ["first", "second", "third"];

属性

Array只有一个属性,就是length,length表示的是数组所占内存空间的数目,而不仅仅是数组中元素的个数,在刚才定义的数组中,b.length的值为8

<script>
var a = new Array("first", "second", "third")
a[48] = "12"
document.write(a.length)
//显示的结果是49
</script>
数组的length属性是可写的,这是一个非常有意思的属性,我们可以通过这种方法来截取数组

<script>
var a = new Array("first", "second", "third")
delete a[1]
document.write(a.length)
//显示的结果是3,说明即使删除也无法改变数组的长度
var a = new Array("first", "second", "third")
a.length = 1
document.write(a.length)
//显示的结果是1,说明只剩下一个元素了
</script>
方法

这里并没有包括IE和FF并不兼容的一些方法:
toString():把数组转换成一个字符串
toLocaleString():把数组转换成一个字符串
join():把数组转换成一个用符号连接的字符串
shift():将数组头部的一个元素移出
unshift():在数组的头部插入一个元素
pop():从数组尾部删除一个元素
push():把一个元素添加到数组的尾部
concat():给数组添加元素
slice():返回数组的部分
reverse():将数组反向排序
sort():对数组进行排序操作
splice():插入、删除或者替换一个数组元素

toString()方法,toLocaleString()方法的作用类似,FF下的作用是完全相同的,IE的话如果元素是字符串,会在“,”后面加上一个空格,如果元素是数字,会扩展到两位小数,两者都会改变字符串的length属性,所以考虑到兼容性,尽量不要使用toLocaleString()方法。

<script>
var a = new Array(1, 2, 3, [4, 5, [6, 7]])
var b = a.toString() //b为字符串形式的 "1, 2, 3, 4, 5, 6, 7"
var c = new Array(1, 2, 3, [4, 5, [6, 7]])
var d = c.toLocaleString() //d为字符串形式的 "1, 2, 3, 4, 5, 6, 7"
//toString()方法和toLocaleString()方法都可以拆解多维数组
</script>
join()方法将数组中的所有元素转换成字符串,然后连接起来,这刚好和String的split()方法是一个相反的操作。join()默认是使用“,”作为分隔符,当然你也可以在方法中指定分隔符

<script>
var a = new Array("first", "second", "third")
var s = a.join("...")
document.write(s)
//显示的结果是“first...second...third”
</script>
pop()方法可以从数组尾部删除若干个元素,push()方法把一个元素添加到数组的尾部,这两个方法刚好是两个相反的操作。两个都是对原来的数组进行操作,但是要注意push()方法返回的是新的数组的长度,而pop()方法则返回被删去的那个元素。

<script>
var a = new Array(1, 2, 3)
var b = a.push(4,5,[6,7]) //a为[1, 2, 3, 4, 5, [6, 7]]  b为6  注意tbpush()方法不会帮你打开一个数组
var c = new Array(1, 2, 3, 4, "first")
var d = c.pop() //c为[1, 2, 3, 4]  d为字符串形式的"first"
</script>
shift()方法可以从数组头部删除一个元素,unshift()方法把若干元素添加到数组的头部,这两个方法刚好是两个相反的操作。两个都是对原来的数组进行操作,但是要注意unshift()方法返回的是新的数组的长度,而shift()方法则返回被删去的那个元素。

<script>
var a = new Array(1, 2, 3)
var b = a.unshift(4,5,[6,7]) //a为[4, 5, [6, 7], 1, 2, 3]  b为6  注意unshift()方法不会帮你打开一个数组,还有就是被插入数值的顺序
var c = new Array("first", 1, 2, 3, 4)
var d = c.shift() //c为[1, 2, 3, 4]  d为字符串形式的"first"
</script>
concat()方法可以返回一个在原有数组上增添了元素的数组,元素用“,”分隔,元素中如果有数组,将被展开并继续添加,但不支持多维数组形式的展开添加

<script>
var a = new Array("first", "second", "third")
s = a.concat("fourth",["fifth", "sixth"],["seventh", ["eighth", "ninth"]])
document.write(s[7])
//显示的结果是“eighth, ninth”,说明“eighth, ninth”是以数组的形式被添加了进去,此是s的值为["first", "second", "third", "fourth", "fifth", "sixth", "seventh", ["eighth", "ninth"]]
</script>
slice()方法返回数组的一个片断,或者说是子数组。slice()的参数表示字数组的始末位置,如果只有一个参数,就表示从该处开始一直取到最后,如果参数出现负数,则表示倒数的某个位置。slice(start,end) //表示数组从从下标为start(包含这个)的地方开始到end(不包含这个)

<script>
var a = new Array(1, 2, 3, 4, 5)
var b = a.slice(3)  //b为[4, 5]
var c = a.slice(-3) //c为[3, 4, 5]
var d = a.slice(1,-1) //d为[2, 3, 4]
var e = a.slice(-3,-1) //e为[3, 4]
</script>
reverse()方法将数组反向排序,他并不创建和返回一个新的数组,而是在原有的数组上进行操作

<script>
var a = new Array("first", "second", "third")
a.reverse()
document.write(a)
//显示的结果是“third,second,first”,这时候数组的顺序已经颠倒了
</script>
sort()方法的作用是对数组进行排序,这是一个非常奇特的方法,我不知道当初创作他的人是出于懒惰还是聪明,这是一个让我印象深刻的方法。
sort()方法的参数是一个有两个参数,并且有返回值的函数,如果返回的值大于零,则说明前一个参数比后一个参数大,等于零则相等,小于零说明前一个参数比后一个小,而相对小的那个参数将出现在排序的前列。
sort()方法直接在数组上进行操作,同时也返回值,但是两者似乎是等价的。sort()方法默认是用字母的顺序进行排序

<script>
var a = new Array(33, 4, 111, 543)
a.sort(way)
function way(x, y){
    if (x % 2 ==0) 
        return 1;
    if (x % 2 !=0)       
        return -1;
}
//排序的结果是使奇数在前偶数在后
</script>
splice()方法的作用是插入、删除或者替换一个数组元素,他tb不光会在原有的数组上进行修改,还会返回被处理掉的内容,因此这是一个功能强大,但是不容易使用的方法,splice()方法用前两个参数进行定位,余下的参数表示插入部分。

<script>
var a = new Array(1, 2, 3, 4, 5)
var b = a.splice(2) //a为[1, 2]  b为[3, 4, 5]
var c = new Array(1, 2, 3, 4, 5)
var d = c.splice(2,2) //c为[1, 2, 5]  d为[3, 4]
var e = new Array(1, 2, 3, 4, 5)
var f = f.splice(-4,2) //e为[1, 4, 5]  f为[2, 3]
var g = new Array(1, 2, 3, 4, 5)
var h = g.splice(-2,-2) //第二个参数表示长度,因此负数在此无效
 
var i = new Array(1, 2, 3, 4, 5)
var j = i.splice(2,2,"first","second","third") //i为[1, 2, "first", "second", "third", 5]  j为[3, 4]  后面部分会自动前后移动,以保持数组的连续性
var k = new Array(1, 2, 3, 4, 5)
var l = k.splice(2,2,["first","second"],"third") //k为[1, 2, ["first", "second"], "third", 5]  l为[3, 4]  splice()方法不会展开数组,只直接写入
</script>

posted @ 2012-08-10 14:57 chen11-1| 编辑 收藏

Java访问WebService返回xml数据

import java.io.IOException;

  import java.io.InputStream;

  import java.net.MalformedURLException;

  import java.net.URL;

  import java.net.URLConnection;

  import java.io.FileNotFoundException;

  import java.io.FileOutputStream;

  import java.io.PrintWriter;

  import org.w3c.dom.Document;

  import org.w3c.dom.DOMException;

  import org.xml.sax.SAXException;

  import javax.xml.parsers.DocumentBuilder;

  import javax.xml.parsers.DocumentBuilderFactory;

  import javax.xml.parsers.ParserConfigurationException;

  import javax.xml.transform.OutputKeys;

  import javax.xml.transform.Transformer;

  import javax.xml.transform.TransformerConfigurationException;

  import javax.xml.transform.TransformerException;

  import javax.xml.transform.TransformerFactory;

  import javax.xml.transform.dom.DOMSource;

  import javax.xml.transform.stream.StreamResult;

  /***

  * @author xuechong

  * 6/11/2010 16:58

  * DomXMLString.java

  * 概述:纯java方式访问远程WebService接口返回的xml格式的数据保存在本地

  */

  public class DomXMLString{

  private static String SERVICES_HOST = "www.webxml.com.cn";

  //远程WebService接口url

  private static String NETDATA_URL = "http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx/getRegionProvince";

  //访问远程WebService接口返回的xml格式的数据保存在本地的绝对路径

  private static String LOCAL_PC_SAVEFILE_URL = "E:dataTest/netDataToLocalFile.xml";

  private DomXMLString(){}

  public static void main(String[] args) throws Exception{

  Document document = getProvinceCode(NETDATA_URL);

  helloOK(document, LOCAL_PC_SAVEFILE_URL);

  }

  /*返回一个Document对象*/

  public static Document getProvinceCode(String netXMLDataURL){

  Document document = null;

  DocumentBuilderFactory documentBF = DocumentBuilderFactory.newInstance();

  documentBF.setNamespaceAware(true);

  try{

  DocumentBuilder documentB = documentBF.newDocumentBuilder();

  InputStream inputStream = getSoapInputStream(netXMLDataURL);    //具体webService相关

  document = documentB.parse(inputStream);

  inputStream.close();

  }catch(DOMException e){

  e.printStackTrace();

  return null;

  }catch(ParserConfigurationException e){

  e.printStackTrace();

  return null;

  }catch (SAXException e){

  e.printStackTrace();

  return null;

  }catch(IOException e){

  e.printStackTrace();

  return null;

  }

  return document;

  }

  /*返回InputStream对象*/

  public static InputStream getSoapInputStream(String url){

  InputStream inputStream = null;

  try{

  URL urlObj = new URL(url);

  URLConnection urlConn = urlObj.openConnection();

  urlConn.setRequestProperty("Host", SERVICES_HOST);    //具体webService相关

  urlConn.connect();

  inputStream = urlConn.getInputStream();

  }catch(MalformedURLException e){

  e.printStackTrace();

  }catch(IOException e){

  e.printStackTrace();

  }

  return inputStream;

  }

  /*访问远程(WebService)xml数据后返回的xml格式字符串并生成为tb本地文件*/

  public static void helloOK(Document document, String savaFileURL){

  TransformerFactory transF = TransformerFactory.newInstance();

  try{

  Transformer transformer = transF.newTransformer();

  DOMSource source = new DOMSource(document);

  transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

  transformer.setOutputProperty(OutputKeys.INDENT, "YES");

  PrintWriter pw = new PrintWriter(new FileOutputStream(savaFileURL));

  StreamResult result = new StreamResult(pw);

  transformer.transform(source, result);

  System.out.println("生成xml文件成功!");

  }catch(TransformerConfigurationException e){

  System.out.println(e.getMessage());

  }catch(IllegalArgumentException e){

  System.out.println(e.getMessage());

  }catch(FileNotFoundException e){

  System.out.println(e.getMessage());

  }catch(TransformerException e){

  System.out.println(e.getMessage());

  }

  }

  }

posted @ 2012-08-09 11:47 chen11-1 阅读(706) | 评论 (0)编辑 收藏

利用字符串连接巧妙解决问题

在应用程序开发过程中,使用的最多的数据类型就是字符串 。在Java语言平台中也是如此。为此掌握字符串的处理技巧,无疑是一位数据库管理员必须要掌握的技能。笔者这里就给大家介绍如何利用字符串连接来解决一些实际的问题。

  一、 字符串连接概述。

  在编写应用程序的时候,我们往往需要将多个字符串连接起来,来完成特定的功能。如现在有两个字符串变量,分别为名字(变量名为name)和年龄(变量名为age)。现在需要在屏幕上输出“我的名字是某某,年龄多少”这个字符串。这个语句该如何写呢?可以写为“我的名字是”+name+“,年龄为”+age。也就是说,在Java语言中,可以通过+号将多个字符串(包括变量)连接成一个字符串。在屏幕上打印出来或者将其赋值给一个新的字符串变量。在进行这个字符串连接的时候,大家需要注意一点。也许有些程序员以前学过其他的开发语言,如SQL语言等等。不同的语言采用的字符串连接字符是不同的。如在SQL中采用的就是||符号。有时候,在开发应用程序的时候,开发人员可能会混淆。如在利用JAVA语言开发应用程序,在代码中需要加入SQL语句从数据库中查询数据。此时就有可能在JAVA代码中和SQL代码中都要用到字符串连接的情况(SQL代码中要将多个字段的内容连接为一个字段)。由于两个代码所采用的连接符号不同,所以可能会搞错。

  二、 利用字符串连接解决分行问题。

  在JAVA开发平台中,可以利用+号将多个字符串合并为一个字符串。不过在实际工作中,这个合并功能往往还可以帮助系统开发人员解决一些棘手的问题。如根据语法规定,JAVA代码中一句相连的字符串不能够分开在两行中书写。这是什么意思呢?如现在有一个字串比较长,为“我的名字叫萝卜,今年29岁,在宁波工作”。如果将这个字符串写在同一行的话,那么这一行会很长,从而影响代码的阅读。但是,笔者希望其在屏幕上输出或者保存在文件中时,则是在同一行。在这种情况下,该如何处理呢?

  确实,这是应用程序开发人员在程序开发过程中经常遇到的一个问题。因为在书写代码时,一个好的程序开发人员应该兼顾到其代码的美观,这有助于程序开发人员阅读代码。毕竟有时候需要多个程序开发人员一同完成某个任务。而不能够只完成某个特定的功能就可以了。此时,为了代码的美观与更好的阅读性,有经验的程序开发人员往往会利用这个+号连接字符来处理这个分行的问题。

  由于对于长的字符串,JAVA代码不能够分为两行来写。否则的话,系统就会提示语法错误。遇到这种情况时,为了提高阅读性,我们往往要求程序开发人员把他们分为两行,同时利用加号将他们连接起来。如下所示:

  “我的名字叫萝卜,今年29岁,”+

  “在宁波工作”

  也就是说,应用程序开发人员可以使用+号将两个字符串连接起来,然后在+号处换行,从而将两个字符串连接起来。此时,JAVA编译器会认为这是合法的,允许程序开发人员采用这个技巧来对字符串进行合理的分行。所以说,利用字符串连接可以有效的解决分行问题。

  三、 利用字符串连接来实现数据类型的自动转换。

  在各种开发语言中,都有各种各样的数据类型。有时候为了满足特定的需要,要对他们进行数据类型的转换。如需要将数字类型的数据转换为字符串类型的数据;再如将某个字符串“2432”转换为数值类型的数据。在JAVA语言的开发平台中,数据类型的转换有两种方式,分别为隐式转换与显示转换。一般情况下,从低级数据类型向高级类型的转换,系统会自动进行转换,而不需要程序员进行任何的手工操作或者指定。这个从低级数据类型向高级数据类型的转换,就叫做隐式转换。在对数据类型进行转换的时候,程序开发人员需要遵守严格的规则,否则的话容易出现错误。如从低精度数据类型向高精度数据类型转换的时候,则永远不会发生溢出,通常情况下都会成功。而如果把高精度数据类型转换为低精度数据类型的话,则就会发生溢出错误,从而导致部分信息丢失,甚至无法正常转换。

  不过在JAVA开发平台中,除了以上这两种转换方式外,还有一种数据类型的转换方法。就是如果某个字符串和某个变量一起(这个变量可能是数值型的数据或者是日期型的数据)利用+号连接起来时,系统会自动对这个变量进行转换,会将其转换为字符串数据类型,然后再与原先的字符串连接起来。

  也就是说,字符串也可同其他基本数据类型进行连接。如果将字符串同这些数据类型进行连接时(如数值型的数据),会将这些数据直接转换成字符串。如上面那个例子中“我的名字是”+name+“,年龄为”+age这个字符串,age这个变量为数值型的数据,而其他为字符串的数据。现在程序开发人员就可以利用这个+号将不同数据类型的字符串连接起来。不过最后的数据类型都会字符串的数据类型。即系统会先将变量age转换为字符型数据,然后再跟其他字符串数据类型进行连接。那么在后台中,这到底是如何实现呢?如果在利用+号来进行连接的时候,如果某个变量不是字符串的数据类型,则系统回自动调用一个toSring方法,将非字符串的数据类型转换成字符串的数据类型,再进行合并。由于这个过程是系统自动完成的,所以程序开发人员需要特别的注意。一般来说,只要将+号运算符的一个操作数是字符串,编译器就会将另一个操作数转换成字符串形式。所以程序开发人员应谨慎地将其他数据类型与字符串相连,以免得到意想不到的结果。如将这tb个利用+号连接起来的内容,如果其中有个操作数是字符型数据的话,那么将其赋值给数值型的变量时,就会导致莫名其妙的问题。这种问题很可能存在。因为这个+号,除了可以连接字符串,还可以用来对数值型的数据进行四则运法运算。所以,在进行四则运法运算时,要确保各个操作数都是数值型的。否则的话,这个后果就可想而知了。

  另外需要再提一句的是,如果在进行加法运算时,需要注意其数据类型的隐式转换。如现在有三个操作数,其中两个操作数其小数点保留两位;另外一个操作数其小数点保留为1位。而最后赋值给一个变量,其保留小数位数3位。这就是一个涉及到不同精度的数据类型的数值型数据转换问题。那么最终的结果是什么呢?笔者这里卖个关,大家若想知道结果的话,可以回去编个程序测试一下。有时候,笔者告诉你们答案,读者并不一定记得住。所以,笔者在这里就点倒为止。

  总之一句话,+号这个连接符号,不仅可以连接各个字符串,而且还用来完成分行与数据类型的隐式转换。为此笔者建议,各个程序开发人员需要注意这方面细节,一定能够获得不少的收获。

posted @ 2012-08-09 11:43 chen11-1 阅读(277) | 评论 (0)编辑 收藏

简单介绍J2EE应用的五种核心策略

对于J2EE,我们知道当开发应用时,在架构设计阶段的决定将对应用的性能和可扩展性产生深远的影响。现在当开发一个应用项目时,我们越来越多地注意到了性能和可扩展性的问题。应用性能的问题比应用功能的不丰富问题往往更为严重,前者会影响到所有用户,而后者只会影响到碰巧使用该功能的那些用户。
  作为应用系统的负责人,一直被要求"要少花钱多办事"----用更少的硬件,更少的网络带宽,以及更短的时间完成更多的任务。J2EE通过提供组件方式和通用的中间件服务是目前首选的最优方式。而要能够构建一个具有高性能和可扩展性的J2EE应用,需要遵循一些基本的架构策略。
  缓存(Caching)
  简单地说,缓存中存放着频繁访问的数据,在应用的整个生命周期中,这些数据存放在持久性存储器或存放在内存中。在实际环境中,典型的现象是在分布式系统中每个JVM中有一个缓存的实例或者在多个JVM中有一个缓存的实例。
  缓存数据是通过避免访问持久性存储器来提高性能的,否则会导致过多的磁盘访问和过于频繁网络数据传输。
  复制
  复制是通过在多台物理机器上创建指定应用服务的多个拷贝来获得整体更大吞吐效率。理论上看,如果一个服务被复制成两个服务,那么系统将可处理两倍的请求。复制是通过单一服务的多个实例的方式从而减少每个服务的负载来提高性能的。
  并行处理
  并行处理将一个任务分解为更为简单的子任务,并能够同时在不同的线程中执行。
  并行处理是通过利用J2EE层执行模式的多线程和多CPU特点来提高性能。tb与使用一个线程或CPU处理任务相比,以并行方式处理多个子任务可以使操作系统在多个线程或处理器中进行分配这些子任务。
  异步处理
  应用功能通常被设计为同步或串行方式。异步处理只处理那些非常重要的任务部分,然后将控制立即返回给调用者,其他任务部分将在稍后执行。
  异步处理是通过缩短那些在将控制返回给用户之前必须处理的时间来提高性能的。虽然都做同样多的事情,但是用户不必等到整个过程完成就可以继续发出请求了。
  资源池
  资源池技术使用的是一套准备好的资源。与在请求和资源之间维持1:1的关系的不同,这些资源可被所有请求所共享。资源池的使用是有条件的,需要衡量下面两种方式的代价:
  A、维持一套可被所有请求共享资源的代价
  B、为每个请求都重新创建一个资源的代价
  当前者小于后者时,使用资源池才是有效率的。
  希望通过本文介绍的是J2EE中的五个核心策略,对你有帮助。

posted @ 2012-08-09 11:42 chen11-1 阅读(224) | 评论 (0)编辑 收藏

Java线程:线程的同步-同步方法

 线程的同步是保证多线程安全访问竞争资源的一种手段。
    线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些原则问题需要考虑,是否有竞争资源被同时改动的问题?
 
    在本文之前,请参阅《Java线程:线程的同步与锁》,本文是在此基础上所写的。
 
    对于同步,在具体的Java代码中需要完成一下两个操作:
    把竞争访问的资源标识为private;
    同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
    当然这不是唯一控制并发安全的途径。
 
    synchronized关键字使用说明
    synchronized只能标记非抽象的方法,不能标识成员变量。
 
    为了演示同步方法的使用,构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。
 
 
/**
* Java线程:线程的同步
*
* @author leizhimin 2009-11-4 11:23:32
*/

public class Test {
        public static void main(String[] args) {
                User u = new User("张三", 100);
                MyThread t1 = new MyThread("线程A", u, 20);
                MyThread t2 = new MyThread("线程B", u, -60);
                MyThread t3 = new MyThread("线程C", u, -80);
                MyThread t4 = new MyThread("线程D", u, -30);
                MyThread t5 = new MyThread("线程E", u, 32);
                MyThread t6 = new MyThread("线程F", u, 21);

                t1.start();
                t2.start();
                t3.start();
                t4.start();
                t5.start();
                t6.start();
        }
}

class MyThread extends Thread {
        private User u;
        private int y = 0;

        MyThread(String name, User u, int y) {
                super(name);
                this.u = u;
                this.y = y;
        }

        public void run() {
                u.oper(y);
        }
}

class User {
        private String code;
        private int cash;

        User(String code, int cash) {
                this.code = code;
                this.cash = cash;
        }

        public String getCode() {
                return code;
        }

        public void setCode(String code) {
                this.code = code;
        }

        /**
         * 业务方法
         * @param x 添加x万元
         */

        public synchronized void oper(int x) {
                try {
                        Thread.sleep(10L);
                        this.cash += x;
                        System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);
                        Thread.sleep(10L);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
        }

        @Override
        public String toString() {
                return "User{" +
                                "code='" + code + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}
 
    输出结果:
线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加“21”,当前用户账户余额为:141
线程E运行结束,增加“32”,当前用户账户余额为:173
线程C运行结束,增加“-80”,当前用户账户余额为:93
线程B运行结束,增加“-60”,当前用户账户余额为:33
线程D运行结束,增加“-30”,当前用户账户余额为:3

Process finished with exit code 0
 
 
    反面教材,不同步的情况,也就是去掉oper(int x)方法的synchronized修饰符,然后运行程序,结果如下:
线程A运行结束,增加“20”,当前用户账户余额为:61
线程D运行结束,增加“-30”,当前用户账户余额为:63
线程B运行结束,增加“-60”,当前用户账户余额为:3
线程F运行结束,增加“21”,当前用户账户余额为:61
线程E运行结束,增加“32”,当前用户账户余额为:93
线程C运行结束,增加“-80”,当前用户账户余额为:61

Process finished with exit code 0
 
    很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,tb并对u的属性做了改动。
 
    可见同步的重要性。
 
 
    注意:
    通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁,但还应该注意的是,同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。
 
void notify()    
                    唤醒在此对象监视器上等待的单个线程。    
void notifyAll()    
                    唤醒在此对象监视器上等待的所有线程。    
void wait()    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。    
void wait(long timeout)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。    
void wait(long timeout, int nanos)    
                    导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。
 
    结合以上方法,处理多线程同步与互斥问题非常重要,著名的生产者-消费者例子就是一个经典的例子,任何语言多线程必学的例子。

posted @ 2012-08-09 11:40 chen11-1 阅读(1199) | 评论 (0)编辑 收藏

推荐使用“百度统计”并且拷贝百度统计的前端框架

     摘要: 本篇博文讲两件事情,一个是推荐在博客园经常写博客的童鞋们一个很棒的工具--“百度统计”,另一个是“拷贝百度统计”的页面框架。   首先讲第一个事情,我的博客里有不少文章都是讲“用户行为分析”的,虽然现在不做这个方向的项目,但是对它的兴趣不减,所以我今天在自己博客里部署了百度的用户行为分析系统“百度统计̶...  阅读全文

posted @ 2012-08-06 13:47 chen11-1| 编辑 收藏

jquery ajax分页插件-简单好用的插件

对1.0版进行了重构,去掉了一些花销的功能,优化了页面样式,现有功能:

1)、一次性把数据加载到页面内存,在页面进行分页。

2)、使用jquery的ajax每次从服务器取数据分页。

3)、支持自定义分页条样式,插件默认实现两种样式可供选择。

 

Html代码  
  1. <table id="table2" >  
  2.     <thead>  
  3.         <tr><th width="200px">网站名称</th>  
  4.                       <th width="100px">网址</th>  
  5.                       <th width="100px">知名度</th>  
  6.                      <th width="120px">访问量</th>  
  7.                 </tr>  
  8.     </thead>  
  9.     <tbody></tbody>  
  10. </table>  
  11.                    
  12. $("#table2").bigPage({ajaxData:{url:"ajax.php"}});   
 

 

更多的例子及代码下载地址:

 

http://bigui4.sinaapp.com/index.html

posted @ 2012-08-06 13:42 chen11-1| 编辑 收藏

主题:Spring3开发实战 之 第四章:对JDBC和ORM的支持

     摘要: 简介 Spring提供的DAO(数据访问对象)支持主要的目的是便于以标准的方式使用不同的数据访问技术,如JDBC,Hibernate或者JDO等。它不仅可以让你方便地在这些持久化技术间切换, 而且让你在编码的时候不用考虑处理各种技术中特定的异常。 一致的异常层次 Spring提供了一种方便的方法,把特定于某种技术的异常,如SQLException, 转化为自己的异常,这种异常属于以 ...  阅读全文

posted @ 2012-08-06 13:41 chen11-1| 编辑 收藏

ASP、JSP、PHP 三种技术比较

  目前,最常用的三种动态网页语言有ASP(Active Server Pages),JSP(JavaServer Pages),PHP (Hypertext Preprocessor)。

  简 介 :

    ASP全名Active Server Pages,是一个WEB服务器端的开发环境,利用它可以产生和执行动态的、互动的、高性能的WEB服务应用程序

 

(1)ASP采用脚本语言VBScript(Java script)作为自己的开发语言。

(2)PHP是一种跨平台的服务器端的嵌入式脚本语言。它大量地借用C, Java和Perl语言的语法, 并耦合PHP自己的特性,使WEB开发者能够快速地写出动态产生页面。它支持目前绝大多数数据库。还有一点,PHP是完全免费的,不用花钱,你可以从PHP官方站点(http: //www.php.net)自由下载。而且你可以不受限制地获得源码,甚至可以从中加进你自己需要的特色。

(3)JSP是Sun公司推出的新一代网站开发语言,Sun公司借助自己在Java上的不凡造诣,将Java从Java应用程序和Java Applet之外,又有新的硕果,就是JSP,Java Server Page。JSP可以在Serverlet和JavaBean的支持下,完成功能强大的站点程序。

三者都提供在 HTML代码中混合某种程序代码、由语言引擎解释执行程序代码的能力。但JSP代码被编译成 Servlet并由Java虚拟机解释执行,这种编译操作仅在对JSP页面的第一次请求时发生。在ASP 、PHP、JSP环境下,HTML代码主要负责描述信息的显示样式,而程序代码则用来描述处理逻辑。普通的 HTML页面只依赖于Web服务器,而ASP 、PHP、JSP页面需要附加的语言引擎分析和执行程序代码。程序代码的执行结果被重新嵌入到HTML代码中,然后一起发送给浏览器。ASP 、PHP、JSP三者都是面向Web服务器的技术,客户端浏览器不需要任何附加的软件支持。

 

  技术特点

  ASP:

  1. 使用VBScript 、 JScript等简单易懂的脚本语言,结合HTML代码,即可快速地完成网站的应用程序。

  2. 无须compile编译,容易编写,可在服务器端直接执行。

  3. 使用普通的文本编辑器,如Windows的记事本,即可进行编辑设计。

  4. 与浏览器无关(Browser Independence), 客户端只要使用可执行HTML码的浏览器,即可浏览Actbive Server Pages所设计的网页内容。Active ServerPages 所使用的脚本语言(VBScript 、 Jscript)均在WEB服务器端执行,客户端的浏览器不需要能够执行这些脚本语言。

  5.Active Server Pages能与任何ActiveX scripting语言兼容。除了可使用VB Script或JScript语言来设计外,还通过plug-in的方式,使用由第三方所提供的其它脚本语言,譬如REXX 、Perl 、Tcl等。脚本引擎是处理脚本程序的COM(Component Object Model) 对象。

  6. 可使用服务器端的脚本来产生客户端的脚本。

  7. ActiveX Server Components(ActiveX 服务器组件 )具有无限可扩充性。可以使用Visual Basic 、Java 、Visual C++ 、COBOL等程序设计语言来编写你所需要的ActiveX Server Component 。

  PHP:

  1. 数据库连接

  PHP可以编译成具有与许多数据库相连接的函数。PHP与MySQL是现在绝佳的群组合。你还可以自己编写外围的函数去间接存取数据库。通过这样的途径当你更换使用的数据库时,可以轻松地修改编码以适应这样的变化。PHPLIB就是最常用的可以提供一般事务需要的一系列基库。但PHP提供的数据库接口支持彼此不统一,比如对Oracle, MySQL,Sybase的接口,彼此都不一样。这也是PHP的一个弱点。

   

  JSP:

  1. 将内容的产生和显示进行分离

  使用JSP技术,Web页面开发人员可以使用HTML或者XML标识来设计和格式化最终页面。使用JSP标识或者小脚本来产生页面上的动态内容。产生内容的逻辑被封装在标识和JavaBeans群组件中,并且捆绑在小脚本中,所有的脚本在服务器端执行。如果核心逻辑被封装在标识和Beans中,那么其它人,如Web管理人员和页面设计者,能够编辑和使用JSP页面,而不影响内容的产生。在服务器端,JSP引擎解释JSP标识,产生所请求的内容(例如,通过存取JavaBeans群组件,使用JDBC技术存取数据库),并且将结果以HTML(或者XML)页面的形式发送回浏览器。这有助于作者保护自己的代码,而又保证任何基于HTML的Web浏览器的完全可用性。

  2. 强调可重用的群组件

  绝大多数JSP页面依赖于可重用且跨平台的组件(如:JavaBeans或者Enterprise JavaBeans)来执行应用程序所要求的更为复杂的处理。开发人员能够共享和交换执行普通操作的组件,或者使得这些组件为更多的使用者或者用户团体所使用。基于组件的方法加速了总体开发过程,并且使得各种群组织在他们现有的技能和优化结果的开发努力中得到平衡。

  3. 采用标识简化页面开发

  Web页面开发人员不会都是熟悉脚本语言的程序设计人员。JavaServer Page技术封装了许多功能,这些功能是在易用的、与JSP相关的XML标识中进行动态内容产生所需要的。标准的JSP标识能够存取和实例化 JavaBeans组件,设定或者检索群组件属性,下载Applet,以及执行用其它方法更难于编码和耗时的功能。

  通过开发定制化标识库,JSP技术是可以扩展的。今后,第三方开发人员和其它人员可以为常用功能建立自己的标识库。这使得Web页面开发人员能够使用熟悉的工具和如同标识一样的执行特定功能的构件来工作。

  JSP技术很容易整合到多种应用体系结构中,以利用现存的工具和技巧,并且扩展到能够支持企业级的分布式应用。作为采用Java技术家族的一部分,以及Java 2EE的一个成员,JSP技术能够支持高度复杂的基于Web的应用。

  由于JSP页面的内置脚本语言是基于Java程序设计语言的,而且所有的JSP页面都被编译成为Java Servlet,JSP页面就具有Java技术的所有好处,包括健壮的存储管理和安全性。

  作为Java平台的一部分,JSP拥有Java程序设计语言“一次编写,各处执行”的特点。随着越来越多的供货商将JSP支持加入到他们的产品中,您可以使用自己所选择的服务器和工具,修改工具或服务器并不影响目前的应用。

 

  应用范围

  ASP是Microsoft开发的动态网页语言,也继承了微软产品的一贯传统,只能执行于微软的服务器产品,IIS(Internet Information Server)(windows NT)和PWS(Personal Web Server)(windows 98)上。Unix下也有ChiliSoft的组件来支持ASP,但是ASP本身的功能有限,必须通过ASP+COM的群组合来扩充,Unix下的COM实现起来非常困难。

  PHP3可在Windows,Unix,Linux的Web服务器上正常执行,还支持IIS,Apache等一般的Web服务器,用户更换平台时,无需变换PHP3代码,可即拿即用。

  JSP同PHP3类似,几乎可以执行于所有平台。如Win NT,Linux,Unix。在NT下IIS通过一个外加服务器,例如JRUN或者ServletExec,就能支持JSP。知名的Web服务器Apache已经能够支持JSP。由于Apache广泛应用在NT、Unix和Linux上,因此JSP有更广泛的执行平台。虽然现在NT操作系统占了很大的市场份额,但是在服务器方面Unix的优势仍然很大,而新崛起的Linux更是来势不小。从一个平台移植到另外一个平台,JSP和JavaBean甚至不用重新编译,因为Java字节码都是标准的与平台无关的。

  性能比较

  有人做过试验,对这三种语言分别做回圈性能测试及存取Oracle数据库测试。

  在循环性能测试中,JSP只用了令人吃惊的四秒钟就结束了20000*20000的回圈。而ASP、PHP测试的是2000*2000循环(少一个数量级),却分别用了63秒和84秒。(参考PHPLIB)。

  数据库测试中,三者分别对 Oracle 8 进行 1000 次 Insert,Update,Select和Delete: JSP 需要 13 秒,PHP 需要 69 秒,ASP则 需要73 秒。

  前景分析   

  目前在国内PHP与ASP应用最为广泛。而JSP由于是一种较新的技术,国内采用的较少。但在国外,JSP已经是比较流行的一种技术,尤其是电子商务类的网站,多采用JSP。

  采用PHP的网站如新浪网(sina)、中国人(Chinaren)等,但由于PHP本身存在的一些缺点,使得它不适合应用于大型电子商务站点,而更适合一些小型的商业站点。首先,PHP缺乏规模支持。其次,缺乏多层结构支持。对于大负荷站点,解决方法只有一个:分布计算。数据库、应用逻辑层、表示逻辑层彼此分开,而且同层也可以根据流量分开,群组成二维数组。而PHP则缺乏这种支持。还有上面提到过的一点,PHP提供的数据库接口支持不统一,这就使得它不适合运用在电子商务中。

  ASP和JSP则没有以上缺陷,ASP可以通过Microsoft Windowsd的COM/DCOM获得ActiveX规模支持,通过DCOM和Transcation Server获得结构支持;JSP可以通过SUN Java的Java Class和EJB获得规模支持,通过EJB/CORBA以及众多厂商的Application Server获得结构支持。

  三者中,JSP应该是未来发展的趋势。世界上一些大的电子商务解决方案提供商都采用JSP/Servlet。比较出名的如IBM的E-business,它的核心是采用JSP/Servlet的Web Sphere。它们都是通过CGI来提供支持的。但去年10月后它推出了Enfinity,一个采用JSP/Servlet的电子商务Application Server,而且声言不再开发传统软件。

  总之,ASP,PHP,JSP三者都有相当数量的支持者,由此也可以看出三者各有所长。正在学习或使用动态页面的朋友可根据三者的特点选择一种适合自己的语言。

 

posted @ 2012-08-03 17:33 chen11-1| 编辑 收藏

反射机制在JDBC连接中的使用

1、数据库当中的表设计



 

2、对应数据表的实体Bean (id为主键)

 

 

 

Java代码  
  1. public class EnginConfigVO {   
  2.     int id = 0;   
  3.     int THREADS_COUNT;   
  4.   
  5.     /**  
  6.      * @return the id  
  7.      */  
  8.     public int primaryGetId() {   
  9.         return id;   
  10.     }   
  11.     /**  
  12.      * @param id the id to set  
  13.      */  
  14.     public void primarySetId(int id) {   
  15.         this.id = id;   
  16.     }   
  17.     /**  
  18.      * @return the tHREADS_COUNT  
  19.      */  
  20.     public int getTHREADS_COUNT() {   
  21.         return THREADS_COUNT;   
  22.     }   
  23.   
  24.     /**  
  25.      * @param tHREADS_COUNT the tHREADS_COUNT to set  
  26.      */  
  27.     public void setTHREADS_COUNT(int tHREADS_COUNT) {   
  28.         THREADS_COUNT = tHREADS_COUNT;   
  29.     }   
  30. }  

由于没有像hibernate那样的注解机制,所以只能在主键的setter和getter方法上动动手脚primaryGetId() ,primarySetId(int id)

 

而实体bean的类名在与数据表的匹配上最后多了“vo” 两个字母,所以在tb下面方法中将这两个字母剪裁掉。

 

反射方法: 

T o 对应的就是实体Bean,这样的方法当然是写在DAO层中,供上层的service调用,传入需要修改的实体Bean

 

 

Java代码  
  1. public <T> void updatePropertiesValues(T o) {   
  2.         StringBuilder sd = new StringBuilder("update ");   
  3.         sd.append(o.getClass().getSimpleName().toLowerCase().substring(0, o.getClass().getSimpleName().length()-2)).append(" ");   
  4.         sd.append("set ");   
  5.         StringBuilder id = new StringBuilder("where ");   
  6.         try {   
  7.             for(Method m : o.getClass().getDeclaredMethods()) {   
  8.                 String name = m.getName();   
  9.                 if (name.startsWith("get")) {   
  10.                     sd.append(name.substring(3).toLowerCase()).append("=");   
  11.                     if(m.invoke(o) instanceof String) {   
  12.                         sd.append("'").append(m.invoke(o)).append("', ");   
  13.                     }else {   
  14.                         sd.append(m.invoke(o)).append(", ");   
  15.                     }   
  16.                 }   
  17.                 if(name.startsWith("primaryGet")) {   
  18.                     id.append(name.substring(10).toLowerCase()).append("=");   
  19.                     if(m.invoke(o) instanceof String) {   
  20.                         id.append("'").append(m.invoke(o)).append("';");   
  21.                     }else {   
  22.                         id.append(m.invoke(o)).append(";");   
  23.                     }   
  24.                 }   
  25.             }   
  26.             sd.delete(sd.length()-2, sd.length());   
  27.             sd.append(" ");   
  28.             sd.append(id);   
  29.         } catch (IllegalArgumentException e) {   
  30.             e.printStackTrace();   
  31.         } catch (IllegalAccessException e) {   
  32.             e.printStackTrace();   
  33.         } catch (InvocationTargetException e) {   
  34.             e.printStackTrace();   
  35.         }   
  36.            
  37.         executeTrans(sd.toString());   
  38.            
  39.     }  

 这样以后便可以拼凑出完整的sql语句,为我们解决了功能相似代码的冗余。。

 

另外在查找时,我们还可以利用发射机制,将数据库返回的resultset 对象包装成List<T>

Java代码  
  1. public static <T> List<T> getObjectsList(ResultSet rs, Class<T> k)   
  2.             throws SQLException {   
  3.         List<T> bl = new ArrayList<T>();   
  4.         if (rs != null) {   
  5.             while (rs.next()) {   
  6. //              System.out.println("result is not null");   
  7.                 T o = null;   
  8.                 try {   
  9.                     o = k.newInstance();   
  10.                     for (Method m : k.getDeclaredMethods()) {   
  11.                         String name = m.getName();   
  12.                         if (name.startsWith("set")) {   
  13. //                          System.out.println(rs.getObject(name.substring(3)).getClass().getName());   
  14.                             m.invoke(o, rs.getObject(name.substring(3)));   
  15.                         }   
  16.                     }   
  17.                     bl.add(o);   
  18.                 } catch (InstantiationException e) {   
  19.                     e.printStackTrace();   
  20.                 } catch (IllegalAccessException e) {   
  21.                     e.printStackTrace();   
  22.                 } catch (IllegalArgumentException e) {   
  23.                     e.printStackTrace();   
  24.                 } catch (InvocationTargetException e) {   
  25.                     e.printStackTrace();   
  26.                 }   
  27.             }   
  28.             return bl;   
  29.         }   
  30.         return null;   
  31.     }  

这样,我们就可以从底层直接获得包装好的List<T>集合。。不足之处,欢迎大家讨论。。 

posted @ 2012-08-03 17:30 chen11-1| 编辑 收藏

java 单例加锁方法的讨论

java 单例加锁方法:

ScheduleEngine是个单例类,在获得实例的方法getinstance中,两次判断其是否为空,有利于多线程的并发操作。

使得实例化时,只在第一次加锁,这样效率会有提高。

 

Java代码 复制代码 收藏代码
  1. class ScheduleEngine{   
  2.        
  3.         private static Lock instanceLock=new ReentrantLock();   
  4.            
  5.         private ScheduleEngine() {   
  6.             setproperties;   
  7.         }   
  8.            
  9.         public static ScheduleEngine getInstance(int temphandlerType) {   
  10.             if(null==engine) {   
  11.                 instanceLock.lock();   
  12.                 try  
  13.                 {   
  14.                 if(null==engine)   
  15.                 {   
  16.                 handlerType=temphandlerType;   
  17.                 engine=new ScheduleEngine(temphandlerType);   
  18.                 }   
  19.            
  20.                 }   
  21.                 finally  
  22.                 {   
  23.                 instanceLock.unlock();   
  24.                 }   
  25.             }   
  26.             return engine;   
  27.             }    
  28.     }  

 

初始实例化 单例c3p0对象的方法,常用的是

 

Java代码 复制代码 收藏代码
  1. public final class ConnectionManager {   
  2.   
  3.     private static ConnectionManager instance;   
  4.     private static ComboPooledDataSource cpds;   
  5.   
  6.     private static String c3p0Properties;   
  7.   
  8.     /**  
  9.      * 从数据库连接池取连接  
  10.      * @throws Exception  
  11.      */  
  12.     private ConnectionManager() throws Exception {   
  13.         Properties p = new Properties();   
  14.         c3p0Properties = System.getProperty("user.dir") +   
  15.                 "/mom_project_config/database.properties";   
  16. //      p.load(this.getClass().getClassLoader().getResourceAsStream(c3p0Properties));   
  17.         p.load(new BufferedInputStream(new FileInputStream(c3p0Properties)));   
  18. //      String url = p.getProperty("url") + p.getProperty("database");   
  19.         String url = p.getProperty("url") + p.getProperty("database")+"?useUnicode=true&characterEncoding=UTF-8";   
  20.         cpds = new ComboPooledDataSource();   
  21.         cpds.setDriverClass(p.getProperty("driverclass"));   
  22.         cpds.setJdbcUrl(url);   
  23.         cpds.setUser(p.getProperty("user"));   
  24.         cpds.setPassword(p.getProperty("password"));   
  25.         // 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 acquireIncrement   
  26.         cpds.setAcquireIncrement(Integer.valueOf(p   
  27.                 .getProperty("acquireincrement")));   
  28.         // 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 acquireRetryAttempts   
  29.         cpds.setAcquireRetryAttempts(Integer.valueOf(p   
  30.                 .getProperty("acquireretryattempts")));   
  31.         // 两次连接中间隔时间,单位毫秒。Default: 1000 acquireRetryDelay   
  32.         cpds.setAcquireRetryDelay(Integer.valueOf(p   
  33.                 .getProperty("acquireretrydelay")));   
  34.         // 自动提交事务;连接关闭时默认将所有未提交的操作回滚。Default: false autoCommitOnClose   
  35.         cpds.setAutoCommitOnClose(Boolean.valueOf(p   
  36.                 .getProperty("autocommitonclose")));   
  37.         // 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,   
  38.         // 如设为0则无限期等待.单位毫秒,默认为0   
  39.         cpds.setCheckoutTimeout(Integer.valueOf(p   
  40.                 .getProperty("checkouttimeout")));   
  41.         // 每多少秒检查所有连接池中的空闲连接。默认为0表示不检查。Default: 0 idleConnectionTestPeriod   
  42.         cpds.setIdleConnectionTestPeriod(Integer.valueOf(p   
  43.                 .getProperty("idleconnectiontestperiod")));   
  44.         // 最大空闲时间,25000秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 maxIdleTime   
  45.         cpds.setMaxIdleTime(Integer.valueOf(p.getProperty("maxidletime")));   
  46.         // 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 initialPoolSize   
  47.         cpds.setInitialPoolSize(Integer.valueOf(p   
  48.                 .getProperty("initialpoolsize")));   
  49.         // 连接池中保留的最小连接数。   
  50.         cpds.setMinPoolSize(Integer.valueOf(p.getProperty("minpoolsize")));   
  51.         // 连接池中保留的最大连接数。Default: 15 maxPoolSize   
  52.         cpds.setMaxPoolSize(Integer.valueOf(p.getProperty("maxpoolsize")));   
  53.         // JDBC的标准参数,用以控制数据源内加载的PreparedStatement数据.但由于预缓存的Statement属于单个Connection而不是整个连接池.所以   
  54.         // 设置这个参数需要考滤到多方面的因素,如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭.默认为0;   
  55.         cpds.setMaxStatements(Integer.valueOf(p.getProperty("maxstatements")));   
  56.         // 连接池内单个连接所拥有的最大缓存被关闭.默认为0;   
  57.         cpds.setMaxStatementsPerConnection(Integer.valueOf(p   
  58.                 .getProperty("maxstatementsperconnection")));   
  59.         // C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成.扩展这些操作可以有效的提升性能,通过多数程实现多个操作同时被执行.默为为3   
  60.         cpds.setNumHelperThreads(Integer.valueOf(p   
  61.                 .getProperty("numhelperthreads")));   
  62.         // 用户修改系统配置参数执行前最多等待的秒数.默认为300;   
  63.         cpds.setPropertyCycle(Integer.valueOf(p.getProperty("propertycycle")));   
  64.         // 如果设为true那么在取得连接的同时将校验连接的有效性。Default: false testConnectionOnCheckin   
  65.         cpds.setTestConnectionOnCheckin(Boolean.valueOf(p   
  66.                 .getProperty("testconnectiononcheckin")));   
  67.         // 因性能消耗大请只在需要的时候使用它。   
  68.         // 如果设为true那么在每个connection提交的时候都将校验其有效性。   
  69.         // 建议使用idleConnectionTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default:   
  70.         // false testConnectionOnCheckout   
  71.         cpds.setTestConnectionOnCheckout(Boolean.valueOf(p   
  72.                 .getProperty("testconnectionOncheckout")));   
  73.         // 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。   
  74.         // 但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。   
  75.         // 如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false   
  76.         // breakAfterAcquireFailure   
  77.         cpds.setBreakAfterAcquireFailure(Boolean.valueOf(p   
  78.                 .getProperty("breakafteracquirefailure")));   
  79.   
  80.     }   
  81.   
  82.     /**  
  83.      * 获得ConnectionManager单例对象  
  84.      * @return  
  85.      */  
  86.     public synchronized static ConnectionManager getInstance() {   
  87.         if (instance == null) {   
  88.             try {   
  89.                 instance = new ConnectionManager();   
  90.             } catch (Exception e) {   
  91.                 e.printStackTrace();   
  92.             }   
  93.         }   
  94.         return instance;   
  95.     }   
  96.   
  97.     /**  
  98.      * 获得连接  
  99.      * @return  
  100.      */  
  101.     public Connection getContection() {   
  102.         try {   
  103.             return cpds.getConnection();   
  104.         } catch (SQLException e) {   
  105.             e.printStackTrace();   
  106.         }   
  107.         return null;   
  108.     }  

 

在初始化获得单例的方法上面加锁,不利于并发操作的执行,用第一段代码两次判断是否为空的方式,可以减少代码执行中锁的引用。 不足之处烦请朋友们指正。。

posted @ 2012-08-03 17:28 chen11-1 阅读(2335) | 评论 (0)编辑 收藏

java初学者实践教程-网络程序

   Java在网络编程这个地方做的很好,java的主要目的也是为了网络而生的,它能方便的访问网络上的资源。我们这节课来介绍网络通讯的两种机制:URL通信机制,Socket通信机制。

    URL表示了Internet上一个资源的引用或地址。Java网络应用程序也是使用URL来定位要访问的Internet的资源。在jdk里面java.net.URL也是一个类,它来封装URL的一些细节。目前大家可以把URL理解为网址,default.aspx 这就是个URL.http是协议名(超文本传输协议)用“://”隔开www.tbwshc.com 是主机名。Default.aspx是文件名。它的端口号没有写,默认是80.

    实践:

 

import java.net.*;

public class ParseURL {

    public static void main(String[] args) throws MalformedURLException{

       URL url = new URL("http://www.100jq.com:45175/default.aspx");

       System.out.println("协议是 "+url.getProtocol());

       System.out.println("主机是 "+url.getHost());

       System.out.println("文件名是 "+url.getFile());

       System.out.println("端口号是 "+url.getPort());

    }}

/*

   URL这个对象中提供了很多方法像是

   getProtocol()

   getHost()

   getFile()

   getPort()

*/ 

 

    我们可以通过URL对文件或资源读取,tb也可以通过URLConnection读取,也可以通过这个写入数据限于cgi脚本。

    实践:

 

import java.net.*;

import java.io.*;

public class URLConnectionReader {

    public static void main(String[] args) throws IOException {

       URL google = new URL("");

       URLConnection g = google.openConnection();

       BufferedReader in = new BufferedReader(new InputStreamReader(g.getInputStream()));

       String inputLine;

       while ((inputLine=in.readLine())!=null)

           System.out.println(inputLine);

           in.close();

    }} 

 

    URL和URLConnection类提供了较高层次的网络访问。有时候需要进行较低层次的访问。编写C/S模型的程序时,就要使用Socket通信机制了。因为在网络上不一定非得访问文件。

    实践:

 

//先写个客户端的应用

import java.net.*;

import java.io.*;

public class SimpleClient {

 public static void main(String args[]) {

    try {

      // 5432端口打开服务器连接

      // 在这里用localhost127.0.0.1是一个意思

      Socket s1 = new Socket("127.0.0.1", 5432); 

      // 对这个端口连接一个reader,注意端口不能够占用别的

      BufferedReader br = new BufferedReader(

        new InputStreamReader(s1.getInputStream()));

      // 读取输入的数据并且打印在屏幕上

      System.out.println(br.readLine());

      //当完成时关闭流和连接

      br.close();

      s1.close();

    } catch (ConnectException connExc) {

      System.err.println("Could not connect to the server.");

    } catch (IOException e) {

      // ignore

    }}}

//这是服务端的应用

import java.net.*;

import java.io.*;

public class SimpleServer {

 public static void main(String args[]) {

    ServerSocket s = null;

    // 注册服务端口为5432

    try {

      s = new ServerSocket(5432);

    } catch (IOException e) {

      e.printStackTrace();

    }

  // 运行监听器并接收,永远循环下去。因为服务器总要开启的

    while (true) {

      try {

        // 等待一个连接的请求

        Socket s1 = s.accept();

        // 得到端口的输出流

        OutputStream s1out = s1.getOutputStream();

        BufferedWriter bw = new BufferedWriter(

          new OutputStreamWriter(s1out));

        // 发送一个字符串

        bw.write("百家拳软件项目研究室欢迎您!\n");

        // 关闭这个连接, 但不是服务端的socket

        bw.close();

        s1.close();

      } catch (IOException e) {

        e.printStackTrace();

      }}}} 

 

下载 href="http://java.chinaitlab.com/download/07072213182935.rar" target=_blank>上述例子打包下载

    执行这个程序和其它的不太一样,先用javac将两个文件编译之后。然后敲start开启另一个窗口。用start命令开启的窗口继承了原来窗口的特性。如图26-1所示

    图26-1

    接着在原来的窗口上执行服务端程序java SimpleServer.在新窗口中执行java SimpleClient 就会看到结果了。注意如果如果在启动服务端的时候抛出bindException则说明5432这个端口已经被别的程序占用着,改成别的端口号就可以了。通常选用端口的时候,其数字最好不要小于1024,1024一下的端口很多都是专用的端口。

posted @ 2012-08-02 17:02 chen11-1| 编辑 收藏

java初学者实践教程-applet

现在的java界,很多东西叫××let,××let的意思都是些小程序的意思。例如:applet应用程序的小程序,servlet服务器端的小程序,midlet手机中的小程序,portlet门户容器端的小程序。这节我们介绍applet.这个东西用的不是很多,但是在java的体系结构中是很有意义的。这个东西是能够在浏览器里运行的,可以潜入到HTML页面里。我们知道普通的Application要有main()作为入口点运行,而Applet要在浏览器里运行,或者开发时查看的时候用appletviewer运行。举个例子,实践:

 

import java.awt.*;

import java.applet.*;

@SuppressWarnings("serial") //抑制警告

//所有的Applet,都继承了java.applet.Applet

public class HelloApplet extends Applet {

    public void paint(Graphics g){

       g.drawString("百家拳软件项目研究室!",30,30);

    }} 

 

    还需要建立一个html文件,因为刚才说了它可以嵌入在浏览器里面。用记事本建立一个hello.html代码如下:

    <applet code="HelloApplet.class" width=150 heightb=150></applet>

    之后照样用javac编译刚才那个类。最后在命令行中输入appletviewer hello.html可以看到结果。

    这种小程序弥补了B/S模型的不足,用浏览器可以执行客户端的东西。因为它功能强大,所以是个不错的东西。可是功能太强大了,又引发了一些安全性的问题。所以浏览器也会对applet做了一些安全性的限制。Applet还有一种叫做沙箱模型的机制,它使得没有访问权限的资源,不能访问。保证了安全性。同时开发时也不是那么方便。Applet又跨平台的特性。

    而且微软的IE浏览器里面在运行applet的时候速度不是很快,不如activex的方便。界面也不是太漂亮。不过它的这种在浏览器中运行的思想还是比较不错的。

posted @ 2012-08-02 17:00 chen11-1| 编辑 收藏

JAVA基础知识精华总结

 1 、对象的初始化

    (1 )非静态对象的初始化

    在创建对象时,对象所在类的所有数据成员会首先进行初始化。

    基本类型:int 型,初始化为0.

    如果为对象:这些对象会按顺序初始化。

    ※在所有类成员初始化完成之后,才调用本类的构造方法创建对象。

    构造方法的作用就是初始化。

    (2 )静态对象的初始化

    程序中主类的静态变量会在main方法执行前初始化。

    不仅第一次创建对象时,类中的所有静态变量都初始化,并且第一次访问某
类(注意此时未创建此类对象)的静态对象时,所有的静态变量也要按它们在类
中的顺序初始化。

    2 、继承时,对象的初始化过程

    (1 )主类的超类由高到低按顺序初始化静态成员,无论静态成员是否为private.

    (2 )主类静态成员的初始化。

    (3 )主类的超类由高到低进行默认构造方法的调用。注意,在调用每一个
超类的默认构造方法前,先进行对此超类进行非静态对象的初始化。

    (4 )主类非静态成员的初始化。

    (5 )调用主类的构造方法。

    3 、关于构造方法

    (1 )类可以没有构造方法,但如果有多个构造方法,就应该要有默认的构
造方法,否则在继承此类时,需要在子类中显式调用父类的某一个非默认的构造
方法了。

    (2 )在一个构造方法中,只能调用一次其他的构造方法,并且调用构造方
法的语句必须是第一条语句。

    4 、有关public、private 和protected

    (1 )无public修饰的类,可以被其他类访问的条件是:a.两个类在同一文
件中,b.两个类在同一文件夹中,c.两个类在同一软件包中。

    (2 )protected :继承类和同一软件包的类可访问。

    (3 )如果构造方法为private ,那么在其他类中不能创建该类的对象。

    5 、抽象类

    (1 )抽象类不能创建对象。

    (2 )如果一个类中一个方法为抽象方法,则这个类必须为abstract抽象类。

    (3 )继承抽象类的类在类中必须实现抽象类中的抽象方法。

    (4 )抽象类中可以有抽象方法,也可有非抽象方法。抽象方法不能为private.

    (5 )间接继承抽象类的类可以不给出抽象方法的定义。

    6 、final 关键字

    (1 )一个对象是常量,不代表不能转变对象的成员,仍可以其成员进行操
作。

    (2 )常量在使用前必须赋值,但除了在声明的同时初始化外,就只能在构
造方法中初始化。

    (3 )final 修饰的方法不能被重置(在子类中不能出现同名方法)。

    (4 )如果声明一个类为final ,则所有的方法均为final ,无论其是否被
final 修饰,但数据成员可为final 也可不是。

    7 、接口interface (用implements来实现接口)

    (1 )接口中的所有数据均为static和final 即静态常量。尽管可以不用这
两个关键字修饰,但必须给常量赋初值。

    (2 )接口中的方法均为public,在实现接口类中,实现方法必须可public
关键字。

    (3 )如果使用public来修饰接口,则接口必须与文件名相同。

    8 、多重继承

    (1 )一个类继承了一个类和接口,那么必须将类写在前面,接口写在后面,
接口之间用逗号分隔。

    (2 )接口之间可多重继承,注意使用关键字extends.

    (3 )一个类虽只实现了一个接口,但不仅要实现这个接口的所有方法,还
要实现这个接口继承的接口的方法,接口中的所有方法均须在类中实现。

    9 、接口的嵌入

    (1 )接口嵌入类中,可以使用private 修饰。此时,接口只能在所在的类
中实现,其他类不能访问。

    (2 )嵌入接口中的接口一定要为public.

    10、类的嵌入

    (1 )类可以嵌入另一个类中,但不能嵌入接口中。

    (2 )在静态方法或其他方法中,不能直接创建内部类tb对象,需通过手段来
取得。

    手段有两种:

    class A { class B {} B getB () { B b = new B(); return b ; }
} static void m () { A a = new A(); A.B ab = a.getB(); // 或者
是 A.B ab = a.new B (); }

    (3 )一个类继承了另一个类的内部类,因为超类是内部类,而内部类的构
造方法不能自动被调用,这样就需要在子类的构造方法中明确的调用超类的构造
方法。接上例:

    class C extends A.B { C () { new A()。super (); // 这一句就
实现了对内部类构造方法的调用。 } }

    构造方法也可这样写:

    C (A a ) { a.super(); } // 使用这个构造方法创建对象,要写成C
c = new C (a ); a是A 的对象。

    11、异常类

    JAVA中除了RunTimeException类,其他异常均须捕获或抛出。

posted @ 2012-08-02 16:57 chen11-1| 编辑 收藏

经验分享:我的JavaEE学习道路

 从很小都认识苹果机了,我不记得我是否在小学的时候学过Basic,只记得大学实验室里的苹果机我的确是摸过(得益于我是教师子弟,有“特殊待遇”),也看到计算机系的学生们编写的游戏。初中,有了自己的游戏机。玩过魂斗罗,坦克。当时觉得很不过瘾,心想以后能自己编写游戏就好了,于是立志以后做个程序员。

    高考不顺利,只考上了普通学校电力专业。这还是幸亏当时学校的罗老师(那四年,她一直都在帮助我)看到我以前的成绩还不错,决定要下我,否则,我就往下落的更厉害了。电力专业几乎没有计算机课程。等到学校关于自动化的时候,开始接触了汇编,和自学了C.当时很羡慕学计算机的那个女老乡,姓杨,呵呵,因为羡慕,还被别人误认为我喜欢她,其实完全不是,她根本对计算机没有兴趣,毕业后也去了当公务员,可惜啊,早知道如此,她何必要高出几分,占据我喜欢的专业呢,我甚至为此感到暗自不爽。 不过大学还是学到了一些计算机皮毛知识,C程序写的很好,记得写了一个模仿TT的打字程序。汇编也不错,写个文件病毒,源代码10K,编译链接后3K多,很大,AV95能识别出来,我想大概是我写的太烂,别的杀毒程序,象KV300,都不认为这是个病毒。不管怎么样,我没有拿这个干啥坏事情。这始终是不光彩的事情。

    该毕业了,家乡的供电局没能进去。我怨我老妈没有帮我跑关系,其实我跟我老妈都不愿意我去,我老妈是不想让我回铜仁,我自己也不想做电力职工(虽然在我们那是一等的暴有钱的工作),我还是喜欢去做个程序员,为此也退掉了别的电力相关的工作。但是,我始终不到该如何入门。毕业了,门卫老头开始赶我们出去,我工作无着落,同学们都已经回到家乡开始上班了,我还在跟老头打游击。他进我退,他退我上床休息,有次晚上洗澡,被他发现,吓得我光着屁股从三楼跑到5楼,再跑回三楼。呵呵,那时候整个宿舍楼都空了,所以也不算丢脸了。

    好运终于坚持到了,网上碰到一网友,后来我叫他秦哥,他说他需要一个人帮他做个网站。我便毛遂自荐了一下,其实,那时候我不懂做网站,不懂ASP,不过我相信我的能力,果然,一段适应时间后,我成了他得力的帮手,我也开始正式进入程序员这个行业了。相比现在的很多学生,我觉得他们比我幸运多了,在大学的时候都已经学习到很多知识,甚至是已经有一定的实践了。刚毕业就能踏入这行,还能有地方住,要知道我不光要跟老头打游击,有时候还睡在电脑城广场的板凳上,早上起来看的第一眼便是保安和他身边对我俯视眈眈的狼狗。

    搞懂了ASP和网站后,开始考虑学更多的东西,这时候我已经放弃了我编写游戏程序的梦想了,因为我跟本不知道如何去追逐这个梦想。我也放弃了我比较擅长的单片机开发(现在应该叫嵌入式)。我转向了Java.俗话说,女怕嫁错狼,男怕入错行。8年前的这个时候,我算是马马虎虎开始我的JavaEE道路吧,这儿有点体会就是一定要坚持自己的理想,而这个理想,应该能养活你的,能让你有兴趣做的事情。

    初学Java,有些迷惑,当时微软有个VJ++,我先买了一本介绍这样的书看,结构后来发现它主要是桌面程序的,而且,跟我知道的JSP不太一样。当时也没有想到可以找人问或者论坛上发给帖子问。幸好后来明智的转到了JSP,挺简单,跟ASP差不多,tb概念都能通用(毕竟处理的问题都一样嘛),比起现在的孩子来说,我当时学的东西太少了,不用学习hibernate,spring,j2ee,也不用学习ant,Junit什么的,呵呵,关键还是当时书太少,见识少,也没有这么多新玩意。好处就是我能深入JSP技术,为以后理解这些Web框架打下了很好的基础。不象现在的孩子,还搞不懂JSP,就去弄MVC,搞的本末倒置了。

    J2EE技术得到提高得益于后来到了北京,去了ZZ公司,现在看来,好不夸张的说,从这个公司出来的程序员,都有一定创新能力和解决问题能力。一到这公司,就做了一个算是大的项目,几十个人,还包括国防科技大学的数十个博士,当时用到了很多J2EE技术,象EJB,JMS都用到了,当时不懂这些,费了很多力气去学,还好项目本身就是个很好的学习材料。通过专研项目代码学到了不少东西,远比看书强多了。现在的很多培训方式都是通过做独立完成项目来学习技术,这是很有道理的。当时那个项目做了一年,期间我对自己要求蛮高的,总会多学点东西,比如学了EJB 无状态会话Bean,虽然项目没有用到有状态Bean,但还是花时间去搞明白。这个项目期间,头一次知道了还有英文资料这么一说,象什么Weblogic使用说明,Java文档都,我都会强迫自己去看,有时候打印下来,躺在小床,打开台灯看,那感觉真是美阿。

posted @ 2012-08-02 16:56 chen11-1| 编辑 收藏

J2ME编程程序开发平台的概念

对于J2ME编程开发平台,在其他平台风生水起的时候,J2ME编程开发平台似乎很沉寂。本文将简单介绍一下几个J2ME编程开发平台的重要概念。

  内存

  我们一直在强调,移动信息设备的内存非常小,使用起来应该加倍的珍惜,但是我们却很少知道这些内存是如何分类的,下面将做详细的介绍。事实上MIDP设备的内存分为三种,

  1.ProgrammeMemory、Heap、persistentStorage

  ProgrammeMemory是移动信息设备分配给MIDletsuite的空间,因为MIDletsuite是以jar文件进行发布的,所以这个文件的大小可以认为是ProgrammeMemory的大小。一些厂商对ProgrammeMemory的最大值是有限制的,例如我的Nokia6108的最大值是64k,超过的话将不能进行安装。减小MIDletsuite的大小非常重要,一个便捷的方法就是使用混淆器对应用程序进行混淆,这样可以减小jar文件的大小。在以后的文章中我会谈到如何使用Proguard.

  Heap是应用程序在运行过程中存放所创建的对象的存储空间,tb本地变量和成员变量也是放在Heap上的,MIDP设备中提供的Heap空间大概在几十k到几百K.

  PersistentStorage的空间是用来实现MIDP应用程序的本地数据持久性存储的,在RecordManagementSystem从入门到精通中我做了详细的介绍这里就不再多说了。

  2.ConnectedLimitedDeviceConfiguration

  CLDC包括一个Java虚拟机和一系列的基础类,J2ME的专家组经过对移动信息设备进行硬件抽象后得到他们的特点,然后设计并实现了在移动信息设备上运行的java虚拟机,通常我们把它叫做KVM.在CLDC1.0还同时提供了由java.io、java.lang、javax.microediton.io、java.util组成的基础类。在CLDC1.1里面添加了java.lang.ref.

  3.MobileInfomationDeviceProfile

  MIDP是运行在CLDC基础之上的,在MIDP中定义了应用程序的生命周期、用户图形界面、数据管理系统等子集,从而构建起了J2ME平台。通常,J2ME平台由一个CLDC和一个或者多个Profile构成。

posted @ 2012-08-01 16:40 chen11-1 阅读(660) | 评论 (0)编辑 收藏

jQuery绘图插件jCanvas 绘制窗口的技巧分享

 Jquery是继prototype之后又一个优秀的Javascrīpt框架。它是轻量级的js库,除了兼容CSS3外,还兼容各种浏览器+)。jQuery使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。

  HTML5 中新定义的 HTML 元素,可以用来在 HTML 页面中通过 JavaScriptb 绘制图形、制作动画。现在要推荐的 jCanvas 就是一个 jQuery 的绘图插件,它封装了一些绘制图形的方法,只需编写几行代码即可生成图形。

  以下是JCanvas 绘制窗口并对其监听的程序代码分享

  import java.awt.*;

  import javax.swing.*;

  import java.awt.event.*;

  class JCanvas extends JComponent

  {

  public JCanvas()

  {

  setDoubleBuffered(true);

  }

  public void paintComponent(Graphics g)

  {

  Dimension size = getSize();

  g.setColor(getBackground());

  g.fillRect(0,0,size.width,size.height);

  }

  }

  class TestJCanvas

  {

  public static void main(String s[] )

  {

  MyWindowListener l = new MyWindowListener();

  JCanvas c = new JCanvas();

  c.setBackground(Color.yellow);

  JFrame f = new JFrame("Test JCanvas...");

  f.addWindowListener(l);

  f.getContentPane().add(c,BorderLayout.CENTER);

  f.pack();

  f.setSize(500,400);

  f.show();

  }

  }

  class MyWindowListener extends WindowAdapter

  {

  public void windowClosing(WindowEvent e)

  {

  System.exit(0);

  }

  }

posted @ 2012-08-01 16:37 chen11-1 阅读(1845) | 评论 (2)编辑 收藏

JavaScript框架:jQuery选择器精通教程

虽然jQuery上手简单,相比于其他库学习起来较为简单,但是要全面掌握,却不轻松。因为它涉及到网页开发的方方面面,提供的方法和内部变化有上千种之多。初学者常常感到,入门很方便,提高很困难。本文的目标是将jQuery选择器做一个系统的梳理,试图理清jQuery的设计思想,找出学习的脉络,使读者从入门到精通。

  jQuery是什么

  简单的说,jQuery是一个JavaScript框架,它的宗旨是:写更少的代码,做更多的事情。对于Web开发人员而言,jQuery是一个功能强大的JavaScript库,能更加快速开发相关应用,例如AJAX交互,JavaScript动画效果等。对于Web设计师而言,jQuery封装了Javascript源码细节,实现了与HTML标签的有效分离,便于设计师更加专注于Web页面设计效果。基于此,网页的用户体验大大增强,包括网页的交互性,视觉效果等等。

  jQuery的核心设计思想是:选择某个网页元素,然后对其进行某种操作。那么如何选择、定位某个网页元素呢?对于JavaScript开发人员而言,通常的一种手段是document.getElementById()。而在jQuery语法中,使用的是美元符号“$”,等价的选择表达式写法为:

var someElement = $("#myId");

  jQuery之所以称之为“jQuery”,主要就是因为它强大的选择器,也就是Javascript Query的意思。下面,我们具体介绍jQuery选择器相关的设计思想:

  一、jQuery基本选择器

  前面提到,选择器是jQuery的特色。jQuery的基本选择器主要分为tb以下五种类型:

  1. $(“#myId”) // 选择ID为myId的网页元素

  2. $(“标签名”) // 例如$(“div”)获取的就是HTML文档中的所有的div元素的jQuery对象集合

  3. $(“.myClass”) // 获取的是HTML文档中所有的class为“myClass”的元素集合

  4. $(“*”) // 这个获取的是HTML文档中的所有的元素

  5. $(“selector1,selector2,selector3…selectorN “) // 这种选择器叫做组选择器。例如:$(“span,#two”)

  // 选取所有的span标签元素和id=two的元素。

  二、jQuery层次选择器

  无论何时,通过jQuery选择器获取的jQuery对象任何时候都是一组元素。jQuery的层次选择器主要分为以下两种类型:

  1. $(“ancestor descendant”):选取parent元素后所有的child元素。ancestor的中文意思是“祖先”,descendant的中文意思是“后代”。例如:

  $(“body div”) 选取body元素下所有的div元素。

  $(“div#test div”) 选取id为“test”的div所包含的所有的div子元素

  2. $(“parent > child”):选取parent元素后所有的第一个child元素。例如:

  $(“body > div”) 选取body元素下所有的第一级div元素。

  $(“div#test > div”) 选取id为“test”的div所包含的所有的第一级div子元素
 三、jQuery过滤选择器

  jQuery最基本过滤选择器包括:

  1. :first // 选取第一个元素。$(“div:first”)选取所有div元素中的第一个div元素

  2. :last // 选取最后一个元素。$(“div:last”)选取所有div元素中的最后一个div元素

  3. :even // 选取索引是偶数的所有元素。$(“input:even”)选取索引是偶数的input元素。

  4. :odd // 选取索引是奇数的所有元素。$(“input:odd”)选取索引是奇数的input元素。

  5. :eq(index) // 选取索引等于index的元素。$(“input:eq(1)”)选取索引等于1的input元素。

  6. :gt(index) // 选取索引大于index的元素。$(“input:gt(1)”)选取索引大于1的input元素。

  7. :lt(index) // 选取索引小于index的元素。$(“input:lt(3)”)选取索引小于3的input元素。

  jQuery内容过滤选择器,可以轻松地对DOM文档中的文本内容进行筛选,从而准确地选取我们所需要的元素。

  1. :contains(text) // 选取含有文本内容为“text”元素。$(“div:contains(‘你’)”)选取含有文本“你”的div元素。

  2. :empty // 选取不包含子元素和文本的空元素。$(“div:empty”)选取不包含子元素(包括文本元素)的div空元素。

  3. :parent // 选取含有子元素或者文本的元素。$(“div:parent”)选取拥有子元素(包括文本元素)的div元素。

  可以看见,jQuery内容过滤选择器的过滤规则主要体现在它所包含的子元素或文本内容上。

  jQuery可见性过滤选择器的用法如下:

  1. :hidden // 选取所有不可见的元素。$(“:hidden”)选取网页中所有不可见的元素。

  2. :visible // 选取所有可见元素。$(“div:visible”)选取所有可见的div元素。

  jQuery属性过滤选择器的过滤规则是通过元素的属性来获取相应的元素。

  1. [attribute] // 选择拥有此属性的元素。$(“div[id]“)选取拥有属性id的元素。

  2. [attribute=value] // 选取属性值为value的元素。$(“div[name=test]“)选取属性name的值为“test”的div元素。

  3. [attribute!value] // 选取属性值不等于value的元素。

  4.[attribute^=value] // 选取属性的值以value开始的元素。

  5. [attribute$=value] // 选取属性的值以value为结束的元素。

  6. [attribute*=value] // 选取属性的值含有value的元素。

  7. [selector1] [selector2] [selectorN] //复合属性选择器。$(“div[id][name*=test]“)选取拥有属性id,且属性name的值含有“test”的div元素

  jQuery子元素过滤选择器的过滤规则相对于其它的选择器稍微有些复杂。

  1. :nth-child(index/even/odd/equation) // 选取每个父元素下的第index个子元素或者奇偶元素。

  2. :first-child // 选取每个父元素的第一个子元素

  3. :last-child // 选取每个父元素的最后一个子元素

  jQuery表单对象属性过滤选择器主要是对所选择的表单元素进行过滤,例如选择不可用的表单元素、被选中的下拉框、多选框等等。

  1. :enabled // 选取所有可用的表单元素。$(“#form1 :enabled”)选取id为“form1”的表单内的所有可用元素。

  2. :disabled // 选取所有不可用的表单元素。

  3. :checked // 选取所有被选中的元素。$(“input:checked”)选取所有被选中的元素。

  4. :selected // 选取所有被选中的选项元素。$(“select :selected”)选取所有被选中的选项元素(option)。

  jQuery中引入了表单选择器,让我们能极其方便地获取到一个表单中的某个或某类型的元素。

 

posted @ 2012-08-01 16:36 chen11-1 阅读(898) | 评论 (0)编辑 收藏

Java 异步消息处理

在前一节实现异步调用的基础上 , 现在我们来看一下一个完善的 Java 异步消息处理机制 .

[ 写在本节之前 ]

       在所有这些地方 , 我始终没有提到设计模式这个词 , 而事实上 , 多线程编程几乎每一步都在应该设计模式 . 你只要能恰如其份地应用它 , 为什么要在意你用了某某名称的模式呢 ?

       一个说书人它可以把武功招数说得天花乱坠 , 引得一班听书客掌声如雷 , 但他只是说书的 . 真正的武林高手也许并不知道自己的招式在说书人口中叫什么 , 这不重要 , 重要的是他能在最恰当的时机把他不知名的招式发挥到极致 !

       你了解再多的设计模式 , 或你写出了此类的著作 , 并不重要 , 重要的是你能应用它设计出性能卓越的系统 .

 

 

       本节的这个例子 , 如果你真正的理解了 , 不要怀疑自己 , 你已经是 Java 高手的行列了 . 如果你抛开本节的内容 , 五天后能自己独立地把它再实现一次 , 那你完全可以不用再看我写的文章系列了 , 至少是目前 , 我再也没有更高级的内容要介绍了 .

       上节的 Java 异步调用为了简化调用关系 , 很多角色被合并到一个类中实现 , 为了帮助大家改快地抓住核心的流程 . 那么一个真正的异步消息处理器 , 当然不是这样的简单 .

一.    它要能适应不同类型的请求 :

本节用 makeString 来说明要求有返回值的请求 . 用 displayString 来说明不需要返回值的请求 .

二.    要能同时并发处理多个请求 , 并能按一定机制调度 :

本节将用一个队列来存放请求 , 所以只能按 FIFO 机制调度 , 你可以改用 LinkedList, 就可以简单实现一个优先级 ( 优先级高的 addFirst, 低的 addLast).

三.    有能力将调用的边界从线程扩展到机器间 (RMI)

四.    分离过度耦合 , 如分离调用句柄 ( 取货凭证 ) 和真实数据的实现 . 分离调用和执行的过程 , 可以尽快地将调返回 .

 

现在看具体的实现 :

public interface Axman {

  Result resultTest(int count,char c);

  void noResultTest(String str);

}

这个接口有两个方法要实现 , 就是有返回值的调用 resultTest 和不需要返回值的调用

noResultTest, 我们把这个接口用一个代理类来实现 , 目的是将方法调用转化为对象 , 这样就可以将多个请求 ( 多个方法调 ) 放到一个容器中缓存起来 , 然后统一处理 , 因为 Java 不支持方法指针 , 所以把方法调用转换为对象 , 然后在这个对象上统一执行它们的方法 , 不仅可以做到异步处理 , 而且可以将代表方法调用的请求对象序列化后通过网络传递到另一个机器上执行 (RMI). 这也是 Java 回调机制最有力的实现 .

       一个简单的例子 .

       如果 1: 做 A

       如果 2: 做 B

如果 3: 做 C

如果有 1000 个情况 , 你不至于用 1000 个 case 吧 ? 以后再增加呢 ?

所以如果 C/C++ 程序员 , 会这样实现 : (c 和 c++ 定义结构不同 )

 

type define struct MyStruct{

int mark;

(*fn) ();

} MyList;

      

       然后你可以声明这个结构数据 :

       {1,A,

         2,B

         3,C

}

做一个循环 :

for(i=0;i<length;i++) {

       if( 数据组 [i].mark == 传入的值 ) ( 数据组 [i].*fn)();

}

简单说 c/c++ 中将要被调用的涵数可以被保存起来 , 然后去访问 , 调用 , 而 Java 中 , 我们无法将一个方法保存 , 除了直接调用 , 所以将要调用的方法用子类来实现 , 然后把这些子类实例保存起来 , 然后在这些子类的实现上调用方法 :

interface My{

       void test();

}

 

class A implements My{

       public void test(){

              System.out.println(“A”):

}

}

class B implements My{

       public void test(){

              System.out.println(“B”):

}

}

 

class C implements My{

       public void test(){

              System.out.println(“C”):

}

}

 

class MyStruct {

      

       int mark;

       My m;

       public MyStruct(int mark,My m){this.mark = amrk;this.m = m}

}

数组 :

{ new MyStruct(1,new A()),new MyStruct(2,new B()),new MyStruct(3,new C())}

for(xxxxxxxxx) if( 参数 == 数组 [i].mark) 数组 [i].m.test();

 

这样把要调用的方法转换为对象的保程不仅仅是可以对要调用的方法进行调度 , 而且可以把对象序列化后在另一台机器上执行 , 这样就把调用边界从线程扩展到了机器 .

 

回到我们的例子 :

class Proxy implements Axman{

  private final Scheduler scheduler;

  private final Servant servant;

 

  public Proxy(Scheduler scheduler,Servant servant){

    this.scheduler = scheduler;

    this.servant = servant;

  }

  public Result resultTest(int count,char c){

    FutureResult futrue = new FutureResult();

    this.scheduler.invoke(new ResultRequest(servant,futrue,count,c));

    return futrue;

  }

 

  public void noResultTest(String str){

    this.scheduler.invoke(new NoResultRequest(this.servant,str));

  }

}

 

其中 scheduler 是管理对调用的调度 , servant 是真正的对方法的执行 :

 

Servant 就是去真实地实现方法 :

 

class Servant implements Axman{

  public Result resultTest(int count,char c){

    char[] buf = new char[count];

    for(int i = 0;i < count;i++){

      buf[i] = c;

      try{

        Thread.sleep(100);

      }catch(Throwable t){}

    }

    return new RealResult(new String(buf));

  }

 

  public void noResultTest(String str){

    try{

      System.out.println("displayString :" + str);

      Thread.sleep(10);

    }catch(Throwable t){}

  }

}

在 scheduler 将方法的调用 (invkoe) 和执行 (execute) 进行了分离 , 调用就是开始 ” 注册 ” 方法到要执行的容器中 , 这样就可以立即返回出来 . 真正执行多久就是 execute 的事了 , 就象一个人点燃爆竹的引信就跑了 , 至于那个爆竹什么时候爆炸就不是他能控制的了 .

public class Scheduler extends Thread {

  private final ActivationQueue queue;

  public Scheduler(ActivationQueue queue){

    this.queue = queue;

  }

 

  public void invoke(MethodRequest request){

    this.queue.putRequest(request);

  }

 

  public void run(){

    while(true){

 

      // 如果队列中有请求线程 , 测开始执行请求

      MethodRequest request = this.queue.takeRequest();

      request.execute();

    }

  }

}

在 schetbduler 中只用一个队列来保存代表方法和请求对象 , 实行简单的 FIFO 调用 , 你要实更复杂的调度就要在这里重新实现 :

class ActivationQueue{

  private static final int MAX_METHOD_REQUEST = 100;

  private final MethodRequest[] requestQueue;

  private int tail;

  private int head;

  private int count;

 

  public ActivationQueue(){

    this.requestQueue = new MethodRequest[MAX_METHOD_REQUEST];

    this.head = this.count = this.tail = 0;

  }

 

  public synchronized void putRequest(MethodRequest request){

    while(this.count >= this.requestQueue.length){

      try {

        this.wait();

      }

      catch (Throwable t) {}

    }

    this.requestQueue[this.tail] = request;

    tail = (tail + 1)%this.requestQueue.length;

    count ++ ;

    this.notifyAll();

 

  }

 

 

  public synchronized MethodRequest takeRequest(){

    while(this.count <= 0){

      try {

        this.wait();

      }

      catch (Throwable t) {}

 

    }

 

    MethodRequest request = this.requestQueue[this.head];

    this.head = (this.head + 1) % this.requestQueue.length;

    count --;

    this.notifyAll();

    return request;

  }

}

 

为了将方法调用转化为对象 , 我们通过实现 MethodRequestb 对象的 execute 方法来方法具体方法转换成具体对象 :

abstract class MethodRequest{

  protected final Servant servant;

}

posted @ 2012-07-31 15:38 chen11-1 阅读(2570) | 评论 (0)编辑 收藏

Java模拟异步消息的发送与回调

本文的目的并不是介绍使用的什么技术,而是重点阐述其实现原理。

 

一、 异步和同步

讲通俗点,异步就是不需要等当前执行的动作完成,就可以继续执行后面的动作。

 

通常一个程序执行的顺序是:从上到下,依次执行。后面的动作必须等前面动作执行完成以后方可执行。这就是和异步相对的一个概念——同步。

 

案例:

A、张三打电话给李四,让李四帮忙写份材料。

B、李四接到电话的时候,手上有自己的工作要处理,但他答应张三,忙完手上的工作后马上帮张三写好材料,并传真给张三。

C、通完电话后,张三外出办事。

 

说明:

张三给李四通完电话后,就出去办事了,他并不需要等李四把材料写好才外出。那么张三让李四写材料的消息就属于异步消息。

相反,如果张三必须等李四把材料写好才能外出办事的话,那么这个消息就属于同步消息了。

 

二、 异步的实现

传统的程序执行代码都是从上到下,一条一条执行的。

但生活中有很多情况并不是这样,以上的案例中,如果李四需要几个小时以后才能帮张三写好材料的话,那张三就必须等几个小时,这样张三可能会崩溃或者抓狂。

 

这种一条龙似的处理,显示不太合理。

 

可以使用以下办法来处理这种问题:

张三找王五去给李四打电话,等李四写好材料后,由王五转交给张三。这样张三就可以外出办其他的事情了。

 

问题得到了合理的解决,之前张三一条线的工作,由张三和王五两条线来完成了,两边同时进行,彼此不耽误。

 

三、 计算机语言的实现

办法有了,如何用程序来模拟实现呢?

 

A、以前由一个线程来处理的工作,可以通过新增一个线程来达到异步的目的。这也就是JAVA中的多线程技术。

B、最后李四写好的材料必须交给张三,以做他用。这就是回调。

 

回调你可以这样来理解:

A发送消息给B,B处理好A要求的事情后,将结果返回给A,A再对B返回的结果来做进一步的处理。

 

四、 Java代码的实现

A、 回调的实现

 


Java代码tb 
  
public interface CallBack {   
      
    public void execute(Object... objects );   
 
public interface CallBack { public void execute(Object... objects ); }

 

Java是面向对象的语言,因此回调函数就变成了回调接口。

 

B、 消息的发送者

 


Javatb代码
  
public class Local implements CallBack,Runnable{   
       
      
    private Remote remote;   
       
      
    private String message;   
       
    public Local(Remote remote, String message) {   
        super();   
        this.remote remote;   
        this.message message;   
    }   
  
      
    public void sendMessage()   
    {   
          
        System.out.println(Thread.currentThread().getName());   
          
        Thread thread new Thread(this);   
        thread.start();   
          
        System.out.println("Message has been sent by Local~!");   
    }   
  
      
    public void execute(Object... objects {   
          
        System.out.println(objects[0]);   
          
        System.out.println(Thread.currentThread().getName());   
          
        Thread.interrupted();   
    }   
       
    public static void main(String[] args)   
    {   
        Local local new Local(new Remote(),"Hello");   
           
        local.sendMessage();   
    }   
  
    public void run() {   
        remote.executeMessage(message, this);   
           
      

C、 远程消息的接收者

 

 


Java代码
  
public class Remote {   
  
      
    public void executeMessage(String msg,CallBack callBack)   
    {   
          
        for(int i=0;i<1000000000;i++)   
        {   
               
        }   
          
        System.out.println(msg);   
        System.out.println("I hava executed the message by Local");   
          
        callBack.execute(new String[]{"Nice to meet you~!"});   
    }   
       
 
public class Remote { public void executeMessage(String msg,CallBack callBack) { for(int i=0;i<1000000000;i++) { } System.out.println(msg); System.out.println("I hava executed the message by Local"); callBack.execute(new String[]{"Nice to meet you~!"}); } }

  

执行Local类的main方法。

 

注意Local类中红色背景的那行:

remote.executeMessage(message, this);

executeMessage方法需要接收一个message参数,表示发送出去的消息,而CallBack参数是他自己,也就是这里的this。表示发送消息后,由Local类自己来处理,调用自身的execute方法来处理消息结果。

如果这里不是用this,而是用其他的CallBack接口的实现类的话,那就不能称之为“回调”了,在OO的世界里,那就属于“委派”。也就是说,“回调”必须是消息的发送者来处理消息结果,否则不能称之为回调。这个概念必须明确。

posted @ 2012-07-31 15:35 chen11-1 阅读(1553) | 评论 (0)编辑 收藏

java实现异步调用实例

在JAVA平台,实现异步调用的角色有如下三个角色:
 
调用者 取货凭证   真实数据
 
一个调用者在调用耗时操作,不能立即返回数据时,先返回一个取货凭证.然后在过一断时间后
凭取货凭证来获取真正的数据.
 
所以连结调用者和真实数据之间的桥梁是取货凭证.我们先来看它的实现:
 
public class FutureTicket{
 private Object data null;
 private boolean completed false;
 
 public synchronized void makeRealData(){
  if(this.complited) return;
  //获取数据的耗时操作.这里用Sleep代替
  try{
   Thread.sleep(10000);
  }catch(Throwable t){}
  this.data "返回的数据内容";
  this.completed true;
  notifyAll();
 }
 
 public synchronized Object getData(){
  while(!this.completed)){
   try{
    wait();
   }catch(Throwable t){}
  }
  return this.data;
  
 }
 public boolean isCompleted(){
  return this.completed;
 }
}
 
为了简单化说明(不把它们的关系开得复杂),这里用Objectb代替了真实数据.而真实的实现中
我们应该把makeData放在一个真实数据的类中,然后提供一个方法返回真实数据.这样对于真实
数据的处理和取货凭证解耦.
 
对于这个取货凭证,调用者的如何调用是异步调用的关键:
 
publc class Requester{
 public FutureTicket request(){
  final FutureTicket ft new FutureTicket();
  
  //在新线程中调用耗时操作
  new Thread(){
   public void run(){
    ft.makeRealData();
   }
  }.start();
  return ft;
 }
}
在新线程中启动耗时操作后,不等待线程的完成立即返回提货单.
 
然后调用者可以根据ft.isCompleted()来调用getData()获取真实数据.
当然对ft.isCompleted()测试可以按规定时间间隔轮巡(极低级的方案),也可以
在条件不满足时wait(),然后等待makeData的notifyAll();这样你就完成了一个
用JAVA模拟的异步操作.
 

改进:
但这样的调用对于调用者来说仍然要继续控制线程操作.如果调用者是一个资深的
程序员,这当然没有问题.但假如我们把对直接数据的处理委托给取货凭证来做.调用
者直接规定对数据的操作,然后由取货凭证来调用规定的操作,这对于调用者是一个很
好的解脱:
 
interface ProcessData{
 public void process(Onject data);
}
 
public MyProcessData{
 public void process(Object data){
  //你不管什么时候起初数据data被获取了.
  //你只要规定如果获取到数据了如何处理
  
  System.out.println(data.toString() "处理完成...........");
  //insert into dataBase?
 }
}
 
取货凭证在接收调用者请求获取数据时,要知道对获取的数据如何处理的方法:
 
public class FutureTicket{
 private Object data null;
 private boolean completed false;
 private ProcessData pd;
 
 public FutureTicket(ProcessData pd){
  this.pd pd;
 }
 public synchronized void makeRealData(ProcessData pd){
  if(this.complited) return;
  //获取数据的耗时操作.这里用Sleep代替
  try{
   Thread.sleep(10000);
  }catch(Throwable t){}
  this.data "返回的数据内容";
  this.completed true;
  notifyAll();
 }
 
 public synchronized void putData(){
  while(!this.completed)){
   try{
    wait();
   }catch(Throwable t){}
  }
  //return this.data;
  //不用返回了,直接处理
  this.pd.process(this.data);
  // alert(?);
  
 }
 

 //这个方法也可以不要了.
 public boolean isCompleted(){
  return this.completed;
 }
}
 
调用:
 
  final FutureTicket ft new FutureTicket(new ProcessData());
  
  //在新线程中调用耗时操作
  new Thread(){
   public void run(){
    ft.makeRealData();
   }
  }.start();
  ft.putData();
 
OK,你现在可以抽烟,喝酒,泡妞.ft会为你完成所有的工作.

posted @ 2012-07-31 15:16 chen11-1 阅读(22725) | 评论 (9)编辑 收藏

用线程池启动定时器

(1)调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务。
(2)支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式。

Java代码  
  1. Executors.newScheduledThreadPool(3).schedule(new Runnable() {          
  2.          @Override  
  3.            public void run() {   
  4.             System.out.println("响");           
  5.         }   
  6.           }, 10,TimeUnit.SECONDS);       //在10秒后响一次  
 
Java代码  
  1. Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {        //频率   
  2.             @Override  
  3.             public void run() {   
  4.                 // TODO Auto-generated method stub   
  5.             System.out.println("响");           
  6.              }   
  7.            },    
  8.         6,   
  9.         2,   
  10.                 TimeUnit.SECONDS);       //在10秒后响之后,每隔2秒响一次  
 

posted @ 2012-07-27 15:04 chen11-1 阅读(1348) | 评论 (0)编辑 收藏

xml语法 学习

一个xml文件分为几部分内容:
文档声明
元素
属性
注释
CDATA区,特殊字符
处理指令

在编写xml文档,需要先使用文档声明,声明xml文档的类型。
最简单的声明语法:
<?xml version="1.0"?>
用encoding 属性说明文档的字符编码:
<?xml version="1.0" encoding="gb2312"?>
用standalone属性说明文档是否独立:
<?xml version="1.0" encoding="gb2312" standalone="yes"?>

元素
xml元素指的xml文件出现的标签,分为开始标签和结束标签。
一个xml元素可以包含字母,数字以及其他可见字符,但是遵守下面的一些规范:
区分大小写
不能以数字或特殊字符开头
不能以xml开头
不能包含空格
名称中间不能包含冒号

注释
xml文件的注释采用 <!--注释-->

CDATA区别
在编写xml文件时,有些内容可能不想让解析引擎解析执行,而是当作原始内容处理,把这些内容放在CDATA区。
对于CDATA区域内的内容,tb解析程序不会处理,而是直接原封不动的输出。
语法:
<![CDATA[内容]]>


转义字符
对于一些单个字符,如果显示原始样式,可以使用转义形式给于处理。
特殊符号    替代符号
&               &amp
<                &lt
>                &gt
"                &quot

处理指令
处理指令,简称PI。处理指令用来指挥解析引擎如何解析xml文档内容。
处理指令必须要以<?作为开头,以>作为结尾。

posted @ 2012-07-27 15:02 chen11-1 阅读(773) | 评论 (0)编辑 收藏

xml约束

xml约束
编写一个文档来约束一个xml文档的书写规范

常用的约束技术

XML DTD
XML Schema

DTD(Document Type Definition),全程为文档类型定义
举例:
文件清单:book.xml

Java代码  
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE 图书 SYSTEM "book.dtd">          
  3. <图书>   
  4.      <书>   
  5.             <书名>西游记</书名>   
  6.             <作者>吴承恩</作者>     
  7.             <售价>18</售价>     
  8.      </书>   
  9.      <书>   
  10.           <书名>三国演义</书名>   
  11.           <作者>罗贯中</作者>     
  12.           <售价>20</售价>   
  13.      </书>    
  14. </图书>  

   文件清单:book.dtbd

Java代码  
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!ELEMENT 图书 (书+)>   
  3. <!ELEMENT 书 (书名,作者,售价)>   
  4. <!ELEMENT 书名 (#PCDATA)>   
  5. <!ELEMENT 作者 (#PCDATA)>   
  6. <!ELEMENT 售价 (#PCDATA)>  

 

 

ELEMENT 元素

PCDATA 的意思是已经解析的字符数据,文本中的标签会被当作标记来处理,而实体会被展开。
用book.dtd约束book.xml,如果在book.xml的书标签下添加出版日期,会报错,因为文档格式已经固定。
注意:DTD文件应使用utf-8保存或者encoding="gb2312",否则会报错。

引用DTD约束
xml文件使用DOCTYPE声明语句来指明它所遵循的DTD文件,声明语句有两种形式
(1)当引用的文件在本地时 ,采用如下方式:
<!DOCTYPE 文档根节点 system "DTD文件的URL"> 
例如:<!DOCTYPE 图书 SYSTEM "book.dtd">  
(2)当引用的文件是一个公共的文件时 ,采用如下方式:
<!DOCTYPE 文档根节点 public "DTD名称" "DTD文件的URL">
例如:<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
 "http://struts.apache.org/dtds/struts-config_1_3.dtd">
 struts2的DTD文件,一般做框架都会用到DTD

DTD约束语法细节
元素定义
属性定义
实体定义

元素定义
在DTD文件中使用element声明一个xml元素,语法格式所示:
<!ELEMENT 元素名称 元素类型>
(1)元素类型可以是元素内容或者类型
如为元素内容:则需要使用()括起来,如
<!ELEMENT 书 (书名,作者,售价)>
<!ELEMENT 书名 (#PCDATA)>
(2)如为元素类型,则直接书写 ,DTD规范定义如下几种类型:
EMPTY:用于定义空元素,例如<br></br>
ANY:表示元素内容为任意类型
-----------------------------------------------------
元素内容可以使用如下方式,描述内容的组成关系用逗号分隔,表示内容的出现顺序必须与声明时一致
用逗号分隔,表示内容的出现顺序必须与声明时一致。
<!ELEMENT 书 (书名,作者,售价)>
用|分隔,表示任选其一,即多个只能出现一个
<!ELEMENT 书 (书名|作者|售价)>
在元素内容中也可以使用+,*,?等符号表示元素出现的次数:
+:一次或多次(书+)
0:0次或一次(书?)
*:0次或多次(书*)
什么都不写 (书)只出现一次

也可以使用原括号()批量设置,例
<!ELEMENT 书((书名*,作者?售价)*|COMMENT)>

属性定义
xml文档的标签性需要通过ATTLIST为其设置属性
语法格式:
<!ATTLIST 元素名
属性名1 属性值类型 设置说明
属性名2 属性值类型 设置说明
......
>
属性声明举例
<!ATTLIST 图书
  书名 CDATA #REQUIRED
  售价 CDATA #IMPLED
>
对应xml文件:
<图书 书名="三国演义" 售价="20">...</图书>
<图书 书名="西游记" 售价="18">...</图书>
设置说明:
#REQUIRED 必须设置该属性
#IMPLED 可以设置也可以不设置
#FIXED:说明该属性的值固定为一个值
直接使用默认值:在xml中可以设置该值也可以不设置该属性值。如果没设置则使用默认值
举例:
<!ATTLST 图书
书名 CDATA #REQUIRED
售价 CDATA #IMPLED
类别 CDATA #FIXED "文学"
评价 CDATA "好"
>
-----------------------------------------------
CDATA表示属性值为普通文本字符串
ENUMERATED(枚举)
ID,表示设置值为一个唯一值,ID属性的值只能由字母,下划线开始,不能出现空白字符
ENTITY(实体)


实体定义
实体用于为一段内容创建一个别名,以后在xml文档中就可以使用别名引用这段内容
在DTD定义一个实体,一条<!ENTITY>语句用于定义一个实体
实体可分为两种类型:引用实体个参数实体
(1)引用实体主要在xml文档中被应用
语法格式:
<!ENTITY 实体名称 "实体内容">: 直接转变成实体内容
引用方式:
&实体名称;
举例
<!ENTITY name "I am a student">
.....
&name;
(2)参数实体被DTD文件自身使用
语法格式:
<!ENTITY % 实体名称 "实体内容">
&实体名称;

 

在struts1里action标签必须要设置的属性:path

Java代码  
  1. <!ELEMENT action (icon?, display-name?, description?, set-property*, exception*, forward*)>   
  2. <!ATTLIST action         id             ID              #IMPLIED>   
  3. <!ATTLIST action         attribute      %BeanName;      #IMPLIED>   
  4. <!ATTLIST action         className      %ClassName;     #IMPLIED>   
  5. <!ATTLIST action         forward        %RequestPath;   #IMPLIED>   
  6. <!ATTLIST action         include        %RequestPath;   #IMPLIED>   
  7. <!ATTLIST action         input          %RequestPath;   #IMPLIED>   
  8. <!ATTLIST action         name           %BeanName;      #IMPLIED>   
  9. <!ATTLIST action         parameter      CDATA           #IMPLIED>   
  10. <!ATTLIST action         path           %RequestPath;   #REQUIRED>   
  11. <!ATTLIST action         prefix         CDATA           #IMPLIED>   
  12. <!ATTLIST action         roles          CDATA           #IMPLIED>   
  13. <!ATTLIST action         scope          %RequestScope;  #IMPLIED>   
  14. <!ATTLIST action         suffix         CDATA           #IMPLIED>   
  15. <!ATTLIST action         type           %ClassName;     #IMPLIED>   
  16. <!ATTLIST action         unknown        %Boolean;       #IMPLIED>   
  17. <!ATTLIST action         validate       %Boolean;       #IMPLIED>  

posted @ 2012-07-27 15:00 chen11-1 阅读(998) | 评论 (0)编辑 收藏

xml解析方式

xml解析方式
dom:(document objectb model),文档对象模型,是w3c组织推荐的解析的xml的一种方式
sax:(simple api for xml),不是官方标准,但是xml社区事实上的标准,几乎所有的xml解析器都支持它

xml解析开发包
Jaxp(sun公司),dom4j,Jdom

dom和sax解析的原理
      在dom中,解析文档的结构类似为一棵树,文档、文档中的根、元素、元素内容、属性、属性值等都是以对象模型的形式表示的。Dom能够在内存中保存整个文档的模型,可以方便对xml元素。
      当sax分析器对xml文档进行分析时,触发一系列事件,并激活相应的事件处理函数,从上到下的顺序读取,读取一行就处理一行。它不允许对xml文件随机存取,没有把xml文档完全加载到内存,占用内存少。

dom和sax解析方法的区别:
1.dom解析的优点是对文档增删改查比较方便,缺点占用内存比较大。
2.sax解析的优点占用内存少,解析速度快,缺点是只适合做文档的读取,不适合做文档的增删改查。

posted @ 2012-07-27 14:57 chen11-1 阅读(1095) | 评论 (0)编辑 收藏

用 java实现定时重启windows指定服务

package com.test.processManagement;  

 

import java.io.BufferedReader;  

import java.io.FileNotFoundException;  

import java.io.FileOutputStream;  

import java.io.IOException;  

import java.io.InputStream;  

import java.io.InputStreamReader;  

import java.io.PrintStream;  

import java.text.ParseException;  

import java.text.SimpleDateFormat;  

import java.util.Date;  

import java.util.Timer;  

import java.util.TimerTask;  

 

/**
* <title>ServRebootScheWin</title>
*  
* <project>Exam</project>
*  
* <package>com.test.processManagement</package>
*  
* <file>ServRebootScheWin.java</file>
*  
* <date>2012-7-11</date>
*  
* <brief>本程序用于每天定时重启windows系统上的指定服务,并记录日志</brief>
*  
* @author Wero
*  
*/


public
class ServRebootScheWin {  

 

public
static
void main(String[] args) {  

// store the console output


final PrintStream console = System.out;  

if (args.length < 2) {  
            LOG("参数不全,程序将退出...");  
            Runtime.getRuntime().exit(-1);  
        }  

 

final String timeStr = args[0];// 每tb天重启时间(HH:mm:ss)


final String servName = args[1];// 服务名


if (args.length >= 3) {  

try {  
                System.setOut(new PrintStream(new FileOutputStream(args[2])));  
            } catch (FileNotFoundException e) {  
                System.setOut(console);  
                LOG("日志文件无法建立...");  
            }  
        }  

 

// convert time string to Date type

        Date date = null;  

try {  
            date = new SimpleDateFormat("HH:mm:ss").parse(timeStr);  
        } catch (ParseException e1) {  
            LOG("日期格式(HH:mm:ss)错误,程序将退出...");  
            Runtime.getRuntime().exit(-1);  
        }  

 

// schedule the specific windows service to reboot at specific time


// every day

        rebootEveryDayTime(date, servName);  

 

// add shutdown hook to recover system.out to console when program exits

        Runtime.getRuntime().addShutdownHook(new Thread() {  

@Override


public
void run() {  
                System.setOut(console);  
            }  
        });  
    }  

 

private
static
void rebootEveryDayTime(Date date, final String servName) {  

new Timer().schedule(new TimerTask() {  

public
void run() {  

try {  
                    reboot(servName);  
                } catch (Exception e) {  
                    LOG("重启出现异常:" + e.getMessage());  
                }  
            }  
        }, date, 24 * 60 * 60 * 1000);  
    }  

 

private
static
void reboot(String servName) throws IOException, InterruptedException {  
        LOG("重启服务:" + servName);  
        Process procStop;  
        Process procStart;  

int stopState = -1;  

int startState = -1;  

 

// stop the specific service

        procStop = Runtime.getRuntime().exec("net stop \"" + servName + "\"");  


        stopState = getProcExecStat(procStop);  
        LOG(getProcOutput(procStop));  

 

// wait for 10 seconds 


try {  
            Thread.sleep(10 * 1000);  
        } catch (InterruptedException e) {  
            LOG("线程等待时中断...");  
            e.printStackTrace();  
        }  

 

// restart

        procStart=Runtime.getRuntime().exec("net start \"" + servName + "\"");  
        startState = getProcExecStat(procStart);  
        LOG(getProcOutput(procStart));  

 

//if stop exec and start exec both return with failed flag,exists


if (stopState != 0 && startState != 0) {  
            LOG("重启失败,请确认服务名是否有效,程序将退出...");  
        } else {  
            LOG("重启成功.");  
        }  
    }  

 

private
static
int getProcExecStat(Process proc) {  

try {  

return proc.waitFor();  
        } catch (InterruptedException e) {  
            LOG("线程等待时中断...");  
            e.printStackTrace();  
        }  

return -1;  
    }  

 

private
static String getProcOutput(Process proc) throws IOException, InterruptedException {  
        InputStream is = proc.getInputStream();  
        String line;  
        StringBuffer strResult = new StringBuffer();  


        BufferedReader reader = new BufferedReader(new InputStreamReader(is));  

while ((line = reader.readLine()) != null) {  
            strResult.append(line);  
        }  
        is.close();  

 

return strResult.toString().trim();  
    }  

 

private
static
void LOG(String info) {  

if (info != null && !info.equals("")) {  
            System.out.println("windows服务监控器--------" + getCurrentTime() + "----------->" + info);  
        }  
    }  

 

private
static String getCurrentTime() {  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");  

return sdf.format(new Date());  
    }  

 

// public enum ExecuteStates {


//


// SUCCEED(0, ""), STATERR_STOPPED(1, "服务已停止"), STATERR_STATED(3, "服务已开始"),


// STATERR_NOTFOUND(


// 2, "服务名无效");


//


// ExecuteStates(int code, String desc) {


// this.code = code;


// this.desc = desc;


// }


//


// private final int code;


// private final String desc;


//


// // regular get method


// public String getDesc() {


// return desc;


// }


//


// public static String getDescByCode(int code){


// for (ExecuteStates e:ExecuteStates.values()){


// if(e.code==code){


// return e.desc;


// }


// }


// return null;


// }


// }

 


posted @ 2012-07-25 15:30 chen11-1| 编辑 收藏

文件FTP上传支持断点续传demo

package cn.eason.util.common;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;


import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;


/**************************************************************
* 文件名称: ContinueFTP.java
* 功能描述: ftp文件上传功能,依赖commons-net-3.1.jar实现
* 创建日期: 2012-5-21
* 创建地址: 西安
* 作者:  Eric.Hao
**************************************************************/
public class ContinueFTP {
       
        private FTPClient ftpClient = new FTPClient();
       
        public ContinueFTP(){
               
                //设置将过程中使用到的命令输出到控制台
                this.ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
        }


        /**
     * java编程中用于连接到FTP服务器
     * @param hostname 主机名
     * @param port 端口
     * @param username 用户名
     * @param password 密码
     * @return 是否连接成功
     * @throws IOException
     */
         public boolean connect(String hostname,int port,String username,String password)
                 throws IOException {
                 //连接到FTP服务器
                 ftpClient.connect(hostname, port);
                 //如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
                 if(FTPReply.isPositiveCompletion(ftpClient.getReplyCode()))
                 {
                     
                         if(ftpClient.login(username, password))
                         {
                                  return true;
                     }
                 }
               
                          disconnect();
                          return false;


         }


         /**
          * 从FTP服务器上下载文件,支持断点续传功能
          * @param remote 远程文件路径
          * @param local 本地文件路径
          * @param mode tb传输方式:PassiveMode方式,ActiveMode方式
          * @return 是否成功
          * @throws IOException
          */
         public DownloadStatus download(String remote,String local,String mode) throws IOException{


                         //设置ftp传输方式
                             if(mode.equalsIgnoreCase("P")){
                                     //PassiveMode传输
                                     ftpClient.enterLocalPassiveMode();
                             }
                             else {
                                     //ActiveMode传输
                                     ftpClient.enterLocalActiveMode();
                             }
         
                             //设置以二进制流的方式传输
                  ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                  
                  //下载状态
                  DownloadStatus result;   
                  
                  //本地文件列表
                  File f = new File(local);
                  
                  //检查远程文件是否存在   
                  FTPFile[] files = ftpClient.listFiles(new String(remote.getBytes("GBK"),"iso-8859-1"));   


                  if(files.length != 1){
                      System.out.println("远程文件不存在");   
                      return DownloadStatus.Remote_File_Noexist;   
                  }
                  
                  //获得远端文件大小
                  long lRemoteSize = files[0].getSize();
                  
                  //构建输出对象
              OutputStream ut = null ;
              
                  //本地存在文件,进行断点下载 ;不存在则新下载
                  if(f.exists()){
                          
                          //构建输出对象
                      out = new FileOutputStream(f,true);
                      
                      //本地文件大小
                      long localSize = f.length();   


                      System.out.println("本地文件大小为:"+localSize);
                      
                      
                      //判定本地文件大小是否大于远程文件大小   
                      if(localSize >= lRemoteSize){
                          System.out.println("本地文件大于远程文件,下载中止");   
                          return DownloadStatus.Local_Bigger_Remote;   
                      }
                      
                      //否则进行断点续传,并记录状态   
                      ftpClient.setRestartOffset(localSize);   
                      
                      InputStream in = ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));   
                      
                      byte[] bytes = new byte[1024];   
                      long step = lRemoteSize /100;  
                      
                      //存放下载进度
                      long process=localSize /step;   
                      int c;   
                      while((c = in.read(bytes))!= -1){   
                          out.write(bytes,0,c);   
                          localSize+=c;   
                          long nowProcess = localSize /step;   
                          if(nowProcess > process){   
                              process = nowProcess;   
                              if(process % 10 == 0)   
                                  System.out.println("下载进度:"+process);   
                              //TODO 更新文件下载进度,值存放在process变量中   
                          }   
                      }
                      //下载完成关闭输入输出流对象
                      in.close();   
                      out.close();   
                      boolean isDo = ftpClient.completePendingCommand();   
                      if(isDo){   
                          result = DownloadStatus.Download_From_Break_Success;   
                      }else {   
                          result = DownloadStatus.Download_From_Break_Failed;   
                      }   


                  }else {
                          out = new FileOutputStream(f);   
                      InputStream in= ftpClient.retrieveFileStream(new String(remote.getBytes("GBK"),"iso-8859-1"));   
                      byte[] bytes = new byte[1024];   
                      long step = lRemoteSize /100;   
                      long process=0;   
                      long localSize = 0L;   
                      int c;   
                      while((c = in.read(bytes))!= -1){   
                          out.write(bytes, 0, c);   
                          localSize+=c;   
                          long nowProcess = localSize /step;   
                          if(nowProcess > process){   
                              process = nowProcess;   
                              if(process % 10 == 0)   
                                  System.out.println("下载进度:"+process);   
                              //TODO 更新文件下载进度,值存放在process变量中   
                          }   
                      }   
                      in.close();   
                      out.close();   
                      boolean upNewStatus = ftpClient.completePendingCommand();   
                      if(upNewStatus){   
                          result = DownloadStatus.Download_New_Success;   
                      }else {   
                          result = DownloadStatus.Download_New_Failed;   
                      }   
                  }
                  return result;
              }
                
              /**
               * 上传文件到FTP服务器,支持断点续传
               * @param local 本地文件名称,绝对路径
               * @param remote 远程文件路径,使用/home/directory1/subdirectory/file.ext 按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
               * @param mode 传输方式:PassiveMode方式,ActiveMode方式
               * @return 上传结果
               * @throws IOException
               */
              public UploadStatus upload(String local,String remote,String mode) throws IOException{
                  //设置PassiveMode传输
                  ftpClient.enterLocalPassiveMode();
                  //设置以二进制流的方式传输
                  ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                  UploadStatus result;
                  //对远程目录的处理
                  String remoteFileName = remote;
                  if(remote.contains("/")){
                      remoteFileName = remote.substring(remote.lastIndexOf("/")+1);
                      String directory = remote.substring(0,remote.lastIndexOf("/")+1);
                      if(!directory.equalsIgnoreCase("/")&&!ftpClient.changeWorkingDirectory(directory)){
                          //如果远程目录不存在,则递归创建远程服务器目录
                          int start=0;
                          int end = 0;
                          if(directory.startsWith("/")){
                              start = 1;
                          }else{
                              start = 0;
                          }
                          end = directory.indexOf("/",start);
                          while(true){
                              String subDirectory = remote.substring(start,end);
                              if(!ftpClient.changeWorkingDirectory(subDirectory)){
                                  if(ftpClient.makeDirectory(subDirectory)){
                                      ftpClient.changeWorkingDirectory(subDirectory);
                                  }else {
                                      System.out.println("创建目录失败");
                                      return UploadStatus.Create_Directory_Fail;
                                  }
                              }
                                 
                              start = end + 1;
                              end = directory.indexOf("/",start);
                                 
                              //检查所有目录是否创建完毕
                              if(end <= start){
                                  break;
                              }
                          }
                      }
                  }
                     
                  //检查远程是否存在文件
                  FTPFile[] files = ftpClient.listFiles(remoteFileName);
                  if(files.length == 1){
                      long remoteSize = files[0].getSize();
                      File f = new File(local);
                      long localSize = f.length();
                      if(remoteSize==localSize){
                          return UploadStatus.File_Exits;
                      }else if(remoteSize > localSize){
                          return UploadStatus.Remote_Bigger_Local;
                      }
                         
                      //尝试移动文件内读取指针,实现断点续传
                      InputStream is = new FileInputStream(f);
                      if(is.skip(remoteSize)==remoteSize){
                          ftpClient.setRestartOffset(remoteSize);
                          if(ftpClient.storeFile(remote, is)){
                              return UploadStatus.Upload_From_Break_Success;
                          }
                      }
                      //如果断点续传没有成功,则删除服务器上文件,重新上传
                      if(!ftpClient.deleteFile(remoteFileName)){
                          return UploadStatus.Delete_Remote_Faild;
                      }
                      is = new FileInputStream(f);
                      if(ftpClient.storeFile(remote, is)){     
                          result = UploadStatus.Upload_New_File_Success;
                      }else{
                          result = UploadStatus.Upload_New_File_Failed;
                      }
                      is.close();
                  }else {
                      InputStream is = new FileInputStream(local);
                      if(ftpClient.storeFile(remoteFileName, is)){
                          result = UploadStatus.Upload_New_File_Success;
                      }else{
                          result = UploadStatus.Upload_New_File_Failed;
                      }
                      is.close();
                  }
                  return result;
              }
         
     /**
      * 断开与远程服务器的连接
      * @throws IOException
      */
     public void disconnect() throws IOException{
         if(ftpClient.isConnected()){
             ftpClient.disconnect();
         }
     }

posted @ 2012-07-25 15:29 chen11-1 阅读(2211) | 评论 (3)编辑 收藏

SpringBird Erp系统快速开发平台(组织机构,权限管理)、代码生成器……

SpringBird Erp系统快速开发平台基于通用的三层架构,数据访问层采用了无Sql注入风险的IBatis.net,表现层采用了微软最新的Asp.net mvc3 Razor模板解析引擎和轻量级的Jquery easyui,服务层采用了接口编程,整体使用成熟可靠的Ioc、Aop框架Spring.net进行服务层、数据访问层和表现层之间的整合,Erp系统快速开发平台默认包含组织机构和权限管理功能。  安装方式
    1、安装.net framework 4.0
    2、建立数据库Erp,用户名/密码:sa/sa
    3、执行Erp.sql
    4、将文件夹Erp拷贝到C:\inetpub\wwwroot下
    5、打开IIS7管理器,将默认网站下的Erp转换为应用程序,应用程序池选择ASP.NET v4.0集成模式
    6、将文件夹PermissionWs拷贝到C:\inetpub\wwwroot下
    7、打开IIS7管理器,将默认网站下的PermissionWs转换为应用程序,应用程序池选择ASP.NET v4.0 Classic经典模式
    8、在浏览器中输入http://localhost/Erp/,账号/密码:Admin/123456
  版本1.0.0.4,2011-8-11发布 下载  http://files.cnblogs.com/springbird/Erp.1.0.0.4.7z
  SpringBird 代码生成器基于通用的三层架构,数据访问层采用了无Sql注入风险的IBatis.net,表现层采用了微软的WinForm,服务层采用了接口编程,整体使用成熟可靠的Ioc、Aop框架Spring.net进行服务层、数据访问层和表现层之间的整合,模板定义使用NVelocity模板引擎。
  消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。SpringBird 基础平台架构之消息中间件(Mom,Message Oriented Middleware)基于通用的三层架构,tb数据访问层采用了无Sql注入风险的IBatis.net,表现层采用了微软最新的Asp.net mvc3 Razor模板解析引擎和轻量级的Jquery easyui,服务层采用了接口编程,整体使用成熟可靠的Ioc、Aop框架Spring.net进行服务层、数据访问层和表现层之间的整合。

posted @ 2012-07-25 15:27 chen11-1| 编辑 收藏

JAVA SSH框架 学习

在Struts + Spring + Hibernate的组合框架模式中,三者各自的特点都是什么?
Struts 的MVC设计模式可以使我们的逻辑变得很清晰。
Spring 的IOC和AOP可以使我们的产品在最大限度上解藕。
hibernate的当然就是实体对象的持久化了
典型的J2EE三层结构,分为表现层、中间层(业务逻辑层)和数据服务层。三层体系将业务规则、数据访问及合法性校验等工作放在中间层处理。客户端不直接与数据库交互,而是通过组件与中间层建立连接,再由中间层与数据库交互。
表现层是传统的JSP技术,自1999年问世以来,经过多年的发展tb其广泛的应用和稳定的表现,为其作为表现层技术打下了坚实的基础。
中间层采用的是流行的Spring+Hibernate,为了将控制层与业务逻辑层分离,又细分为以下几种。
Web层,就是MVC模式里面的“C”(controller),负责控制业务逻辑层与表现层的交互,调用业务逻辑层,并将业务数据返回给表现层作组织表现,该系统的MVC框架采用Struts。
Service层(就是业务逻辑层),负责实现业务逻辑。业务逻辑层以DAO层为基础,通过对DAO组件的正面模式包装,完成系统所要求的业务逻辑。
DAO层,负责与持久化对象交互。该层封装了数据的增、删、查、改的操作。
PO,持久化对象。通过实体关系映射工具将关系型数据库的数据映射成对象,很方便地实现以面向对象方式操作数据库,该系统采用Hibernate作为ORM框架。
Spring的作用贯穿了整个中间层,将Web层、Service层、DAO层及PO无缝整合,其数据服务层用来存放数据。
一个良好的框架可以让开发人员减轻重新建立解决复杂问题方案的负担和精力;它可以被扩展以进行内部的定制化;并且有强大的用户社区来支持它。框架通常能很好的解决一个问题。然而,你的应用是分层的,可能每一个层都需要各自的框架。仅仅解决UI问题并不意味着你能够很好的将业务逻辑和持久性逻辑和UI 组件很好的耦合。

不可否认,对于简单的应用,采用ASP或者PHP的开发效率比采用J2EE框架的开发效率要高。甚至有人会觉得:这种分层的结构,比一般采用JSP + Servlet的系统开发效率还要低。
笔者从一下几个角度来阐述这个问题。
— 开发效率:软件工程是个特殊的行业,不同于传统的工业,例如电器、建筑及汽车等行业。这些行业的产品一旦开发出来,交付用户使用后将很少需要后续的维护。但软件行业不同,软件产品的后期运行维护是个巨大的工程,单纯从前期开发时间上考虑其开发效率是不理智的,也是不公平的。众所周知,对于传统的ASP和 PHP等脚本站点技术,将整个站点的业务逻辑和表现逻辑都混杂在ASP或PHP页面里,从而导致页面的可读性相当差,可维护性非常低。即使需要简单改变页面的按钮,也不得不打开页面文件,冒着破坏系统的风险。但采用严格分层J2EE架构,则可完全避免这个问题。对表现层的修改即使发生错误,也绝对不会将错误扩展到业务逻辑层,更不会影响持久层。因此,采用J2EE分层架构,即使前期的开发效率稍微低一点,但也是值得的。
— 需求的变更:以笔者多年的开发经验来看,很少有软件产品的需求从一开始就完全是固定的。客户对软件需求,是随着软件开发过程的深入,不断明晰起来的。因此,常常遇到软件开发到一定程度时,由于客户对软件需求发生了变化,使得软件的实现不得不随之改变。当软件实现需要改变时,是否可以尽可能多地保留软件的部分,尽可能少地改变软件的实现,从而满足客户需求的变更?答案是——采用优秀的解耦架构。这种架构就是J2EE的分层架构,在优秀的分层架构里,控制层依赖于业务逻辑层,但绝不与任何具体的业务逻辑组件耦合,只与接口耦合;同样,业务逻辑层依赖于DAO层,也不会与任何具体的DAO组件耦合,而是面向接口编程。采用这种方式的软件实现,即使软件的部分发生改变,其他部分也尽可能不要改变。
注意:即使在传统的硬件行业,也有大量的接口规范。例如PCI接口、显卡或者网卡,只要其遵守PCI的规范,就可以插入主板,与主板通信。至于这块卡内部的实现,不是主板所关心的,这也正是面向接口编程的好处。假如需要提高电脑的性能,需要更新显卡,只要更换另一块PCI接口的显卡,而不是将整台电脑抛弃。如果一台电脑不是采用各种接口组合在一起,而是做成整块,那将意味着即使只需要更新网卡,也要放弃整台电脑。同样,对于软件中的一个个组件,当一个组件需要重构时,尽量不会影响到其他组件。实际上,这是最理想的情况,即使采用目前最优秀的架构,也会有或多或少的影响,这也是软件工程需要努力提高的地方。
技术的更新,系统重构:软件行业的技术更新很快,虽然软件行业的发展不快,但小范围的技术更新特别快。一旦由于客观环境的变化,不得不更换技术时,如何保证系统的改变最小呢?答案还是选择优秀的架构。
在传统的Model 1的程序结构中,只要有一点小的需求发生改变,将意味着放弃整个页面。或者改写。虽然前期的开发速度快,除非可以保证以后永远不会改变应用的结构,否则不要采用Model 1的结构。
采用Hibernate作为持久层技术的最大的好处在于:可以完全以面向对象的方式进行系统分析、系统设计。
DAO模式需要为每个DAO组件编写DAO接口,同时至少提供一个实现类,根据不同需要,可能有多个实现类。用Spring容器代替DAO工厂
通常情况下,引入接口就不可避免需要引入工厂来负责DAO组件的生成。Spring实现了两种基本模式:单态模式和工厂模式。而使用Spring可以完全避免使用工厂模式,因为Spring就是个功能非常强大的工厂。因此,完全可以让Spring充当DAO工厂。
由Spring充当DAO工厂时,无须程序员自己实现工厂模式,只需要将DAO组件配置在Spring容器中,由ApplicationContext负责管理DAO组件的创建即可。借助于Spring提供的依赖注入,其他组件甚至不用访问工厂,一样可以直接使用DAO实例。
优点:
Struts跟Tomcat、Turbine等诸多Apache项目一样,是开源软件,这是它的一大优点。使开发者能更深入的了解其内部实现机制。
除此之外,Struts的优点主要集中体现在两个方面:Taglib和页面导航。Taglib是Struts的标记库,灵活动用,能大大提高开发效率。另外,就目前国内的JSP开发者而言,除了使用JSP自带的常用标记外,很少开发自己的标记,或许Struts是一个很好的起点。
关于页面导航,我认为那将是今后的一个发展方向,事实上,这样做,使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时,这种优势体现得更加明显。
缺点:
Taglib是Struts的一大优势,但对于初学者而言,却需要一个持续学习的过程,甚至还会打乱你网页编写的习惯,但是,当你习惯了它时,你会觉得它真的很棒。
Struts将MVC的Controller一分为三,在获得结构更加清晰的同时,也增加了系统的复杂度。
Struts从产生到现在还不到半年,但已逐步越来越多运用于商业软件。虽然它现在还有不少缺点,但它是一种非常优秀的J2EE MVC实现方式,如果你的系统准备采用J2EE MVC架构,那么,不妨考虑一下Struts。

posted @ 2012-07-25 15:25 chen11-1| 编辑 收藏

AJAX的学习与理解 .

1>:今天重新回到对AJAX的认识,首先从AJAX的由来说起,由于感觉自己才疏学浅,

      我感觉只有了解了WEB的发展历史,我们才能更好地理解并运用AJAX。

2>:其实AJAX最大的应用就是我们要理解XMLHttpRequest这个对象。XMLHttpRequest可以提供不重新加载页面的情况下更新网页,在页面加载后在客户端向服务器请数 据, 在  页面加载后在服务器端接受数据,在后台向客户端发送数据。XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步返回 Web 服务器的响应,并且能以文本或者一个 DOM 文档形式返回内容。

3>:我们如何创建一个XMLHttpRequest对象呢?

     首先我们要知道XMLHttpRequest对象开始时有微软开发的一个基于IE5,IE6的一个插件,所以他在IE浏览器中肯定有自己的创建方式,IE中把他实现成一个ActiveX对象,但其他浏览器如果也想使用  XMLHttpRequest对象,就必须依靠JAVASCRIPT创建本地对象。我们在使用时不需要判断浏览器的类型,只需要看浏览器提供对ActiveX的支持。

下面就是XMLHttpRequest的创建方式:

[javascript] view plaincopyprint?
01.function createXHR() 
02.{ 
03. if(window.ActiveXObject) 
04. { 
05.  xmlHttpRequest=new ActiveXObject("Microsoft.XMLHTTP"); 
06. } 
07. else if(window.XMLHttpRequest) 
08. { 
09.  xmlHttpRequest=new XMLHttpRequest(); 
10. } 
11. if(null==xmlHttpRequest) 
12. {  
13.  alert("浏览器不支持"); 
14. } 
15.} 
 function createXHR()
 {
  if(window.ActiveXObject)
  {
   xmlHttpRequest=new ActiveXObject("Microsoft.XMLHTTP");
  }
  else if(window.XMLHttpRequest)
  {
   xmlHttpRequest=new XMLHttpRequest();
  }
  if(null==xmlHttpRequest)
  {
   alert("浏览器不支持");
  }
 }4>:介绍XMLHttpRequest的常用属性、方法:

百度其实是个好东西,我没必要复制粘贴:大家可以参考http://baike.baidu.com/view/1105115.htm

5>:XMLHttpRequest的应用:

其实要理解AJAX并学会运用我们只要掌握三方面内容,第一就是XMLHttpRequest对象的创建。第二就是如何从表单获取值并传到服务器。第三就是如何从后台获取数据显示在前台。大家想想AJAX的应用是不是就是围绕这三方面。

下面我们来简单说一下如何从表单获取值的问题:其实只要稍微了解点JavaScript的热你都知道我们DOM文档模型,通过操纵文档对象模型中对象的属性并调用其方法,可以使脚本按照一定的方式显示Web页并与用户的动作进行交互。

我们常用的就是document.getElementById("id").value.来获取网页中的数据。其他的大家用到就百度。

我们获取到值之后就是把这个值传给后台:这里我们一般采用get:方式提交,采用url传参。通过调用:xmlHttpRequest.open("get","url?paramater="+value,true);

最后就是如何从后台获取值传到前台显示:这里是通过xmlHttpRequest.responseText(返回一个字符串)xmlHttpRequest.responseXML返回一个XML文件。

我们可以通过解析这两种值插入到页面中:通常我们用到的显示在htbml中方式是:通过document.getElementById("id").value=xmlHttpRequest.responseText;

或者document.getElementById("id").innerHTML=xmlHttpRequest.responseText;

知道了这些我们先做一个简单地例子:

[javascript] view plaincopyprint?
01.<script type="text/javascript"> 
02.    var xmlHttpRequest=null; 
03.    function createXHR() 
04.    { 
05.        if(window.ActiveXObject) 
06.        { 
07.            xmlHttpRequest=new ActiveXObject("Microsoft.XMLHTTP"); 
08.        } 
09.        else if(window.XMLHttpRequest) 
10.        { 
11.            xmlHttpRequest=new XMLHttpRequest(); 
12.        } 
13.        if(null!=xmlHttpRequest) 
14.        {    
15.            var v1=document.getElementById("num1").value; 
16.            var v2=document.getElementById("num2").value; 
17.            xmlHttpRequest.open("get","servlet/AjaxServlet?v1="+v1+"&v2="+v2,true); 
18.            xmlHttpRequest.onreadystatechange=callback; 
19.            xmlHttpRequest.send(null); 
20.        } 
21.    } 
22.    function callback() 
23.    { 
24.        if(xmlHttpRequest.readyState==4) 
25.        { 
26.            if(xmlHttpRequest.status==200) 
27.            { 
28.                var responseText=xmlHttpRequest.responseText; 
29.                document.getElementById("num3").innerHTML=responseText;//innerHTML不能小写  
30.            } 
31.        } 
32.    } 
33.    </script> 
<script type="text/javascript">
 var xmlHttpRequest=null;
 function createXHR()
 {
  if(window.ActiveXObject)
  {
   xmlHttpRequest=new ActiveXObject("Microsoft.XMLHTTP");
  }
  else if(window.XMLHttpRequest)
  {
   xmlHttpRequest=new XMLHttpRequest();
  }
  if(null!=xmlHttpRequest)
  { 
   var v1=document.getElementById("num1").value;
   var v2=document.getElementById("num2").value;
   xmlHttpRequest.open("get","servlet/AjaxServlet?v1="+v1+"&v2="+v2,true);
   xmlHttpRequest.onreadystatechange=callback;
   xmlHttpRequest.send(null);
  }
 }
 function callback()
 {
  if(xmlHttpRequest.readyState==4)
  {
   if(xmlHttpRequest.status==200)
   {
    var responseText=xmlHttpRequest.responseText;
    document.getElementById("num3").innerHTML=responseText;//innerHTML不能小写
   }
  }
 }
 </script>[html] view plaincopyprint?
01.<form method="" action=""> 
02.    <table border="1" borderstyle="solid" cellpacing="0" > 
03.        <tr><td><input type="text" id="num1"/></td></tr> 
04.        <tr><td><input type="text" id="num2"/></td></tr> 
05.        <tr><td><input type="button" value="submit" onclick="createXHR()"/></td></tr> 
06.        <tr><td><span id="num3"></span></td></tr> 
07.    </table>  
08.   </form> 
<form method="" action="">
    <table border="1" borderstyle="solid" cellpacing="0" >
     <tr><td><input type="text" id="num1"/></td></tr>
     <tr><td><input type="text" id="num2"/></td></tr>
     <tr><td><input type="button" value="submit" onclick="createXHR()"/></td></tr>
     <tr><td><span id="num3"></span></td></tr>
    </table>
   </form>以上代码有借鉴http://blog.csdn.net/csh624366188/article/details/7670500大家也可以去看。

 

还有两哥问题一:如何将结果显示在下拉列表,第二如何读取XML文件,这个稍后介绍。

 

posted @ 2012-07-24 16:49 chen11-1 阅读(1579) | 评论 (0)编辑 收藏

关于Jquery中的链式编程和动画效果(注意事项) .

1.

其实我本身对于链式编程一直不怎么感冒,因为看起来逻辑不怎么清晰,今天就将一个链式编程给拆开了,结果发现,其实JQuery的链式编程原来还是不一样的,这让我想起了java中的链式编程,以前也没怎么考虑过,现在想想,原来自己一直在误区当中。


想要说的就是,Jquery中的链式编程,其执行顺序是从后往前执行的。例如,在做类似qq的分组显示时,用Jquery语句:

$(this).siblings("li[class!=header]").hide().next().show("fast");

就是不可以的,要把它换为

$(this).next().show().siblings("li[class!=header]").hide();


2.

关于动画效果,开始也没怎么考虑,今天发现还是不太一样的。

比如说:

$(this).next().show();

alert(123)

 

$(this).next().show("fast");

alert(123)

二者是不一样的,没有加fast,其立即执行,因此,很tb可能是先执行后面的语句,再执行本语句。也就是说,加了速度的参数,产生的是一段延迟。

posted @ 2012-07-24 16:48 chen11-1| 编辑 收藏

函数式编程的10年演化:越来越纯

在过去10年中,函数式编程的定义一直在慢慢改变,在“Wikipedia page on Functional Programming”上可以显著地反映出来。

在2003年8月14号之前的定义是:函数式编程作为一种模式,强调函数的使用。例如在2011年10月14号,维基百科用了220个单词进行定义,在开头这样描述到:

函数式编程是一种编程风格,它关注的是函数表达式的计算,而不是执行命令。在这些语言上,表达式把函数与基本功能相结合。

函数式编程语言是一种支持和鼓励将电脑运算当成函数计算,比较古老的函数式编程莫过于Lisp,较现代的Scheme,ML,Haskell,Erlang,Clean。Lisp是第一个函数式编程语言,它和Scheme,ML,Haskell一样。

这样的定义一直维持到2003年8月14号,新百科全书在内容制作上有了结构化调整。Luxor把新百科全书的定义与维基百科相融合,创建了一个新的页面并且用了1640个单词进行重新定义,开头定义到:

函数式编程是一种编程范式,把计算看作是一种数学函数计算。与命令式编程相比,tb函数式编程关注的是函数表达式的计算而不是执行命令。在这些语言上,表达式把函数与基本功能相结合。

在这一点上,函数式编程并不强调函数的使用,相反,它意味着编程要与数学函数相结合。在这个定义里还隐藏着另外一个意思:函数越来越纯粹。这一条在定义里并没有明确的表达出来。直到2006年5月29号:

函数式编程是一种编程范式,把计算看作是一种数学函数计算。函数式编程关注的是函数定义而不是实现状态机,与过程式编程相比,它强调命令的连序执行。一个纯函数程序不会去修改状态产生的值(适用于命令式编程),它会构造新的值(但不会覆盖现有值)。

对函数式编程或函数式编程语言没有统一的共识。函数式编程语言的重要特征:高阶和一级函数、闭包、递归。其他还包括编程语言连续性、Hindley-Milner类型诊断系统、懒惰计算和单体。

这里还有一些小区别,关于(普通)函数编程和“纯”形式的函数编程,避免使用不稳定的状态。在2006年5月29号01:07分,在没有任何征兆的情况下,一个叫ideogram的用户进行了微小但是非常有意义的修改:

•函数式编程是一种编程风格,把计算看作是一种数学函数计算并且避免状态和可变数据。
•函数式编程关注函数定义,与过程式编程相比,它强调命令的连序执行。
•函数式编程的依赖λ演算(lambda calculus),Lisp和较新的Haskell。常常提到的是避免状态和副作用(它提供对引用透明),高阶函数,递归和闭包。
该账户仅仅在编辑前几天创建,后来就一直处于禁止状态。

最近,也就是在2012年7月14号,定义页面又出现了新的变化:

在计算机科学领域,函数式编程是一种编程范式,把计算看作是一种数学函数计算并且避免状态和可变数据。它强调函数的应用,与命令式编程语言相比,强调状态的变化。函数式编程根源于λ 演算,一个正式的函数式编程系统在20世纪30年代被开发,用来研究函数的定义,函数的应用和递归。许多函数式编程语言可以看作是对λ演算进行的阐述。

在实际操作中,数学函数和“函数”概念之间在命令式编程上的差异主要表现为命令式函数编程存在改变程序状态值这一缺点。因此,他们缺乏引用透明,相同的语言表达式根据执行应用程序状态,在不同的时间可以产生不同的值。相反,在函数代码上,函数的输出值仅仅依赖于函数的输入参数,所以,输入同一个参数X两次,那么输出结果都是Y。消除副作用也更容易理解和预测程序的行为,这是函数编程语言发展的一个重要推动。

[1]Hudak, Paul。“概念, 发展和函数式编程语言的应用”。ACM Computing Surveys 21 (3): 359–411。上述内容的含义是什么?这一变化是如何产生的?我们正卷入一场(小的)科学革命中,幕后推手又是谁?

根据最新的定义,ML和Lisp不在是函数式语言。用这些语言的子函数去写一些程序来避免可变状态的使用。第一个需求是在ML中使用“ref”或者在Lisp中使用分配和构造突变。然而,对于ML和Lisp来说并不足够,许多标准库都存在负面作用,对于任何IO库来说,都是如此。结果就是,程序在一个范围内可以很方便地编写比较单一的功能与风格,但这也是相当有限的。

 

posted @ 2012-07-24 16:45 chen11-1| 编辑 收藏

Java程序员应该了解的10个面向对象设计原则

面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton、Decorator、Observer这样的设计模式,而不重视面向对象的分析和设计。甚至还有经验丰富的Java程序员没有听说过OOPS和SOLID设计原则,他们根本不知道设计原则的好处,也不知道如何依照这些原则来进行编程。

众所周知,Java编程最基本的原则就是要追求高内聚和低耦合的解决方案和代码模块设计。查看Apache和Sun的开放源代码能帮助你发现其他Java设计原则在这些代码中的实际运用。Java Development Kit则遵循以下模式:BorderFactory类中的工厂模式、Runtime类中的单件模式。你可以通过Joshua Bloch的《Effective Java》一书来了解更多信息。我个人偏向的另一种面向对象的设计模式是Kathy Sierra的Head First Design Pattern以及Head Firstb Object Oriented Analysis and Design。

虽然实际案例是学习设计原则或模式的最佳途径,但通过本文的介绍,没有接触过这些原则或还在学习阶段的Java程序员也能够了解这10个面向对象的设计原则。其实每条原则都需要大量的篇幅才能讲清楚,但我会尽力做到言简意赅。

原则1:DRY(Don't repeat yourself)

即不要写重复的代码,而是用“abstraction”类来抽象公有的东西。如果你需要多次用到一个硬编码值,那么可以设为公共常量;如果你要在两个以上的地方使用一个代码块,那么可以将它设为一个独立的方法。SOLID设计原则的优点是易于维护,但要注意,不要滥用,duplicate 不是针对代码,而是针对功能。这意味着,即使用公共代码来验证OrderID和SSN,二者也不会是相同的。使用公共代码来实现两个不同的功能,其实就是近似地把这两个功能永远捆绑到了一起,如果OrderID改变了其格式,SSN验证代码也会中断。因此要慎用这种组合,不要随意捆绑类似但不相关的功能。

原则2:封装变化

在软件领域中唯一不变的就是“Change”,因此封装你认为或猜测未来将发生变化的代码。OOPS设计模式的优点在于易于测试和维护封装的代码。如果你使用Java编码,可以默认私有化变量和方法,并逐步增加访问权限,比如从private到protected和not public。有几种Java设计模式也使用封装,比如Factory设计模式是封装“对象创建”,其灵活性使得之后引进新代码不会对现有的代码造成影响。

原则3:开闭原则

即对扩展开放,对修改关闭。这是另一种非常棒的设计原则,可以防止其他人更改已经测试好的代码。理论上,可以在不修改原有的模块的基础上,扩展功能。这也是开闭原则的宗旨。

原则4:单一职责原则

类被修改的几率很大,因此应该专注于单一的功能。如果你把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能,这时就需要新一轮的测试来避免可能出现的问题。

原则5:依赖注入或倒置原则

这个设计原则的亮点在于任何被DI框架注入的类很容易用mock对象进行测试和维护,因为对象创建代码集中在框架中,客户端代码也不混乱。有很多方式可以实现依赖倒置,比如像AspectJ等的AOP(Aspect Oriented programming)框架使用的字节码技术,或Spring框架使用的代理等。

原则6:优先利用组合而非继承

如果可能的话,优先利用组合而不是继承。一些人可能会质疑,但我发现,组合比继承灵活得多。组合允许在运行期间通过设置类的属性来改变类的行为,也可以通过使用接口来组合一个类,它提供了更高的灵活性,并可以随时实现。《Effective Java》也推荐此原则。

原则7:里氏代换原则(LSP)

根据该原则,子类必须能够替换掉它们的基类,也就是说使用基类的方法或函数能够顺利地引用子类对象。LSP原则与单一职责原则和接口分离原则密切相关,如果一个类比子类具备更多功能,很有可能某些功能会失效,这就违反了LSP原则。为了遵循该设计原则,派生类或子类必须增强功能。

原则8:接口分离原则

采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。设计接口很棘手,因为一旦释放接口,你就无法在不中断执行的情况下改变它。在Java中,该原则的另一个优势在于,在任何类使用接口之前,接口不利于实现所有的方法,所以单一的功能意味着更少的实现方法。

原则9:针对接口编程,而不是针对实现编程

该原则可以使代码更加灵活,以便可以在任何接口实现中使用。因此,在Java中最好使用变量接口类型、方法返回类型、方法参数类型等。《Effective Java》 和《head first design pattern》书中也有提到。

原则10:委托原则

该原则最典型的例子是Java中的equals() 和 hashCode() 方法。为了平等地比较两个对象,我们用类本身而不是客户端类来做比较。这个设计原则的好处是没有重复的代码,而且很容易对其进行修改。

总之,希望这些面向对象的设计原则能帮助你写出更灵活更好的代码。理论是第一步,更重要的是需要开发者在实践中去运用和体会。

 

posted @ 2012-07-24 16:43 chen11-1 阅读(931) | 评论 (0)编辑 收藏

对互联网创新的看法 .


最近对如何在互联网创新进行了许多讨论,现在记下一些感想:
一:前提条件
最好是能解决市场中没有满足的需求,首先确定使用用户群,之后定向推广,有很大可能用户爆发增长。


能促进人与人之间的沟通,拉近人与人之间的距离,打破沟通障碍;


产品要简单,上手快。不要有太多门槛,每一级门槛都会流失大部分用户。


在自己强项和特长的基础上做产品;


创新不是凭空创造,更多的是很多小的改进的积累;


产品使用符合大众习惯,而不是开发者习惯。


小产品如果要快速发展,最好抓住简单,有趣,奇特,搞怪。。。这些特点去设计,产品可以简单,但是要做出精品,好用而且让人印象深刻。需要抛弃复杂思路和做法。


最好能帮助用户解决改进生活中的一个或者几个问题,而不是纯娱乐。


二:关于创造

创造不是凭空的,而是许多在现有基础上小的创新积累的结果,量变产生质变,才会产生伟大的创造。去了解硅谷公司的创新历史会知道这一点。
点点网是给小资的,4399(www.4399.com)是为更广大的底层用户服务的,同样服务底层的还有 掷出窗外(http://www.zccw.info/)后两者的访问量都是巨大的,掷出窗外服务器都顶不住了。这是两种创新思维,前者紧跟硅谷;tb后者贴近中国底层基础需求;两种思路,两种设计和展示方式。
互联网网站需要操作简单,设计简单,零门槛;手机app也需要很简单,越简单,功能单一,会容易传播;
网站展示应该简单,复杂的结果是使用门槛高,传播门槛高,挡住了大部分用户。需要的砍掉多余的设计,为用户呈现月简单越好,设计颜色也应该尽量的少,例如thislife和google的白色很百搭很大众。
网站的名字,如果类似 google 或者自己创造的一个词,没有特殊意义才会更容易赋予很多意义。
手机视频app的爆发期在3G用户量超过2G并且资费很便宜的时候,目前还处于前夜。相信很多大公司在做准备了。
互联网的另外很多机会在垂直领域,这些领域需要有很强的专业背景和资源,才可能在这个领域内做到最好;

三 关于国内网的差距
国外会以互动方式展示功能,功能使用的时候再展现出来,例如评论;而不是把所有功能都列出来web1.0的展示方式;

国外的网站在模拟现实环境,例如把照片排列在一面墙上,网站背景模拟的是白色的墙,照片挂到墙上,贴近现实生活,用户易懂。
网站也在尽可能在模拟触屏的滑动效果;

很简单易懂的表达方式,团队有很高的交互式设计能力;很有苹果风格;

功能用到的时候再展现给用户,而不是摆出所有功能让用户自助选择;如果把网站比喻为餐厅的话,国外很多网站的服务很到位,服务水平很高,国内网站像是小吃部,体验不好。

网站,app设计的最终目标是与真实环境越像越好,用户上手也会很快,他们的设计思路是:尽可能的模拟真实世界,无论是视觉上还是操作上;

差距在哪里? 技术上差距应该不大,差距在同一个思想如何表达给用户,国外强的地方是他们的表达方式和思维方式;点点网许朝军有过一句话:the product is the story we tell to the world。 国内很多创业者有很多故事,但是故事讲的方式不好,理解和认可的人少。

 

posted @ 2012-07-20 15:06 chen11-1 阅读(878) | 评论 (0)编辑 收藏

Spring3注解零配置 .

     摘要: 我们在以前学习Spring的时候,其所有的配置信息都写在applicationContext.xml里,大致示例如下: java代码: 查看复制到剪贴板打印 <beans> <beanname="ds"class="org.apache.commons.dbcp.BasicDataSource"> <propertyname="driverCl...  阅读全文

posted @ 2012-07-20 15:03 chen11-1| 编辑 收藏

傻逼的老板,苦逼的程序员

 

近日较火的一帖子就是京东员工按时上下班遭“被离职”。话说一员工入职一个多月以来,每天保质保量完成任务,没迟到过,没早退过,按时上下班。因为没有加班,被京东开除,领导的理由是:按时上下班,没有奉献精神。坑爹啊。

 

人剥削人的社会现象是永远都存在的,然而当文明越来越发达的今天,人对于自我的思考也就越多。以现在社会生产力,人的生存早已不成问题,当人们解决了生存问题,解决了生活问题,肯定就会对自己长久的发展开始了思考。

 

对比着目前的状态,思索未来的道路。

 

若现在的企业还沿用着古老的方式,压榨剥削着人们,我认为这个企业就是在为自己挖掘坟墓,努力地挖坑,然后把自己给埋了。埋了就埋了,还想着在坑爹,谁会呆在这样的公司。而现在,这种傻逼的公司,傻逼的老板太多了,你按时下班他就会感觉着心里不舒服,觉得给你发那多工资亏了,只有在对你不断的压榨中才能找到心理的平衡。不去寻求高效的管理,高效的开发,单纯的靠加班又不给加班费来降低成本,完成目标,这样的企业能有什么竞争力。

 

试想一下,头天晚上加班到深夜,第二天还要早早上班,即便不昏昏欲睡,效率也会大大降低,就这样磨洋工,混日子,哪还有什么工作积极性,如此恶性循环,长此以往,身体也会搞垮掉。没有周末,没有节假日,每天还要加班的深夜,谁堪承受。身体是革命的本钱,把身体都累垮了,tb给你再多的钱又有什么用呢,只有工作没有休息,赚了钱都消费不出去,那赚钱还有什么意义。苦逼的程序员啊,加班加班,30岁的年龄80岁的心脏。看一博客,说有一个漂亮女人和一个帅哥在吃KFC,说了句“没事的,我们还有的是时间,我老公是做IT的,他现在还在加班呢!”,老公是做IT的……听着欲哭无泪啊。谁想加班,谁还会去做IT……

 

看一个员工的能力,要看他单位时间内完成的工作量,八小时工作的时间就精力高度集中,高效完成任务,下班就好好休息。学要学好,玩要玩好。如果仅凭他是否加班来评定那将是多么扯淡,当一员工老是要加班才能完成别人都能按时完成的任务,那就要对他的能力产生怀疑了,或者是他在别人工作时打酱油去了。若一个公司有加班费,而员工本来没什么任务也呆在公司混时间赚加班费,这样的员工也不会有什么大的作为,甚至要对其人品产生质疑。这样的加班对公司,对员工都没什么意义。

 

所以啊请不要成为傻逼的公司,请不要再做傻逼的老板了。IT公司,想要真的有所作为,请不要再加班了,有木有,有木有啊……

 

如真是紧急情况需要加班,也不能说不行,但也要通过调休或者加薪的方式对员工补偿回来。并且加班的持续时间不能过长,要是连续加班两周估计就会有人受不了,更要把这种紧急加班情况减少到最低。

posted @ 2012-07-20 15:01 chen11-1 阅读(988) | 评论 (0)编辑 收藏

Javascript实现DIV滚动自动滚动到底部

一个比较特殊的客户要求,在一个页面用表格显示数据,数据量不是很多,不希望使用浏览器的滚动条,只能在Div中滚动table中的数据,但是有个特殊的要求,就是必须将滚动条自动滚动到底部。

查询了一下相关的资料,Div没有自动滚动的属性,只能模拟鼠标的滚动来tb现实想要的效果。

关键的部分部分在这里:div.scrollTop = div.scrollHeight;

下面是具体实现的精简代码:

复制代码
 1 <html>
2 <body>
3 <div id="divDetail" style="overFlow-y:scroll; width:250px;height: 200px;">
4 <table style="border:1px solid; ">
5 <tr><td>id</td><td>name</td><td>age</td><td>memo</td></tr>
6 <tr><td>000001</td><td>name1</td><td>24</td><td>memomemomemomemomemo</td></tr>
7 <tr><td>000002</td><td>name2</td><td>23</td><td>memomemomemomemomemo</td></tr>
8 <tr><td>000003</td><td>name3</td><td>23</td><td>memomemomemomemomemo</td></tr>
9 <tr><td>000004</td><td>name4</td><td>23</td><td>memomemomemomemomemo</td></tr>
10 <tr><td>000005</td><td>name5</td><td>23</td><td>memomemomemomemomemo</td></tr>
11 <tr><td>000002</td><td>name2</td><td>23</td><td>memomemomemomemomemo</td></tr>
12 <tr><td>000003</td><td>name3</td><td>23</td><td>memomemomemomemomemo</td></tr>
13 <tr><td>000004</td><td>name4</td><td>23</td><td>memomemomemomemomemo</td></tr>
14 <tr><td>000005</td><td>name5</td><td>23</td><td>memomemomemomemomemo</td></tr>
15 </table>
16 </div>
17 </body>
18 <script type="text/javascript" defer>
19 var div = document.getElementById('divDetail');
20
21 div.scrollTop = div.scrollHeight;
22 //alert(div.scrollTop);
23 </script>
24 </html>
复制代码

 

其实,实现是很简单的但是一般很少有这种需求,期间还是走了一些弯路。

posted @ 2012-07-19 17:32 chen11-1 阅读(804) | 评论 (0)编辑 收藏

SQLServer2008 动态SQL实践

SQL Server的动态SQL功能听说了很长时间了,但是一直没有实践过。通常的项目中都是在程序中拼写SQL然后送到SQL Server中去执行,不过这样对于复杂一些或者数据量大的SQL来说不是最优,使用存储过程就是一种很好的选择方案。

一个最简单的动态SQL

exec sp_executesql N'select * from  emp'

当然我们使用动态SQL不是来做这样简单的事情。

 

看看下面这个,通常我们存储过程都是这样的。

复制代码
 1 CREATE PROCEDURE [dbo].[mytest]
2 @id nchar(5),
3 @s_date nchar(10),
4 @e_date nchar(10)
5 AS
6
7 declare @sql varchar(4000)
8
9 begin
10 select * from emp
11 where work_date >= ' + @s_date + ' and work_date <= ' + @e_date + '
12 end
复制代码

 

但是如果因为业务需要传进来的参数可能为空,这个时候就需要进行判断,但是上面的代码无法完成这种需求。我们这里只是一种假设,实际的情况可能比这个复杂一些。这时候我们就需要动态SQL了。

 

下面这个存储过程通过使用动态SQL就很容易实现了我们程序上的这个需要。

复制代码
CREATE PROCEDURE [dbo].[mytest]
@id nchar(5),
@s_date nchar(10),
@e_date nchar(10)
AS

declare @sql varchar(4000)

begin
set @sql='select * from emp '

if (@s_date <> '') and (@e_date <> '')
set @sql = @sql + ' where work_date >= ''' + @s_date + ''' and work_date <= ''' + @e_date + ''''
else
set @sql = @sql + ' where work_date is null'
end
复制代码

 

这里要注意一个问题,还是先看例子

复制代码
 1 CREATE PROCEDURE [dbo].[mytest]
2 @id nchar(5),
3 @s_date nchar(10),
4 @e_date nchar(10)
5 AS
6
7 declare @sql varchar(4000)
8
9 begin
10 set @sql='select * from emp
11 where id=''1'' and work_date is null'
12 end
复制代码

 

注意第11行

 

 

set @sql='select * from emp
11 where id=''1'' and work_date= ''' + @s_date  + ''''

 如果写成

 

set @sql='select * from emp
11 where id='1' and work_date= ' + @s_date  + '

就是错误的,这个想必大家都明白原因,只是写的时候往往会忽略这个问题,这里tb提醒一下大家。

 

另一个需要注意的是字符型的变量的判断,要使用''来判断是否为空而不能使用 is not null

    if (@s_date <> '') and (@e_date <> '')
        set @sql = @sql + '    where work_date >= ''' + @s_date + ''' and work_date <= ''' + @e_date + ''''
    else
        set @sql = @sql + '    where work_date is null'

 

最后一个例子,在游标中使用动态SQL,因为在游标中不能直接使用动态SQL,所以需要借助临时表来,完成动态SQL在游标中的循环执行。

复制代码
 1 BEGIN TRANSACTION
2
3 --定义临时表
4 create table #tmp_table
5 (
6 id nchar(5),
7 ...
8
9 )
10
11 --执行动态SQL将记录插入到临时表中
12 insert into #tmp_table (id,...) EXECUTE sp_executesql @sql
13
14 --在游标中便利游标
15 Declare cur_tmp Cursor Scroll
16 For
17 select (id,...) from #tmp_table
18 OPEN cur_tmp
19
20 Fetch next from cur_tmp
21
22 into @id,...
23
24 while @@fetch_status=0
25 begin
26
27
28   ...
29 fetch next from cur_tmp
30 into @id,...
31
32
33 end
34 CLOSE cur_tmp
35 drop table #tmp_table
36
37 Deallocate cur_tmp
38
39
40
41 if @@error <> 0
42 begin
43
44 ROLLBACK TRANSACTION
45
46 if not (select object_id('Tempdb..#tmp_table')) is null
47 drop table #tmp_table
48
49 COMMIT TRANSACTION
复制代码


动态SQL使储存过程的实现更加的灵活和方便,但是由于SQL不是程序代码在测试的时候会不方便一些,但是它会使程序的执行效率大大提高还是从这一点上说还是值得的。

posted @ 2012-07-19 17:30 chen11-1| 编辑 收藏

Eclipse中SVN插件的安装方式

大部分时候我们都可以通过在线的方式安装SVN插件:

在Eclipse 中,Help -> Software Updates -> Find and Install...菜单下。

在弹出对话框中的输入框中输入http://subclipse.tigris.org/update作为URL添加New Remote Site。

就可以让Eclipse自动下载为你安装SVN插件了,安装成功后重新启动Eclipse就OK!

 

还有一种方式就是下载压缩包离线安装,这个也是一种安装SVN插件的方式。

Eclipse4.3之前我们可以直接将压缩包加压后覆盖到Eclipse根目录下,但是4.3之后这种方式就无法安装了,你能够看到在eclipse的根目录中有一个Dropins目录,就是它直接将解压后的全部插件文件复制到这个目录下,tb重新启动Eclipse就安装成功了!!!

posted @ 2012-07-19 17:27 chen11-1| 编辑 收藏

设计模式之Adapter(适配器)

定义:
将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份.

为何使用?
我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。 怎么办?

使用Adapter,在这两种接口之间创建一个混合接口(混血儿).

如何使用?
实现Adapter方式,其实"think in Java"的"类再生"一节中已经提到,有两种方式:组合(composition)和继承(inheritance).


假设我们要打桩,有两种类:方形桩 圆形桩.
public class SquarePeg{
  public void insert(String str){
    System.out.println("SquarePeg insert():"+str);
  }

}

public class RoundPeg{
  public void insertIntohole(String msg){
    System.out.println("RoundPeg insertIntoHole():"+msg);
}
}

现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设RoundPeg我们没有源代码,或源代码我们不想修改,那么我们使用Adapter来实现这个应用:

public class PegAdapter extbends SquarePeg{

  private RoundPeg roundPeg;

  public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)

  public void insert(String str){ roundPeg.insertIntoHole(str);}

}

在上面代码中,RoundPeg属于Adaptee,是被适配者.PegAdapter是Adapter,将Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)进行适配.实际上这是将组合方法(composition)和继承(inheritance)方法综合运用.

PegAdapter首先继承SquarePeg,然后使用new的组合生成对象方式,生成RoundPeg的对象roundPeg,再重载父类insert()方法。从这里,你也了解使用new生成对象和使用extends继承生成对象的不同,前者无需对原来的类修改,甚至无需要知道其内部结构和tb源代码.

如果你有些Java使用的经验,已经发现,这种模式经常使用。

进一步使用
上面的PegAdapter是继承了SquarePeg,如果我们需要两边继承,即继承SquarePeg 又继承RoundPeg,因为Java中不允许多继承,但是我们可以实现(implements)两个接口(interface)

public interface IRoundPeg{
  public void insertIntoHole(String msg);

}

public interface ISquarePeg{
  public void insert(String str);

}

下面是新的RoundPeg 和SquarePeg, 除了实现接口这一区别,和上面的没什么区别。
public class SquarePeg implements ISquarePeg{
  public void insert(String str){
    System.out.println("SquarePeg insert():"+str);
  }

}

public class RoundPeg implements IRoundPeg{
  public void insertIntohole(String msg){
    System.out.println("RoundPeg insertIntoHole():"+msg);
  }
}

下面是新的PegAdapter,叫做two-way adapter:

public class PegAdapter implements IRoundPeg,ISquarePeg{

  private RoundPeg roundPeg;
  private SquarePeg squarePeg;

  // 构造方法
  public PegAdapter(RoundPeg peg){this.roundPeg=peg;}
  // 构造方法
  public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)

  public void insert(String str){ roundPeg.insertIntoHole(str);}

}

还有一种叫Pluggable Adapters,可以动态的获取几个adapters中一个。使用Reflection技术,可以动态的发现类中的Public方法。

posted @ 2012-07-18 16:36 chen11-1| 编辑 收藏

设计模式之Composite(组合)

Composite定义:
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.

Composite比较容易理解,想到Composite就应该想到树形结构图。组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。可以用牵一动百来形容。

所以Composite模式使用到Iterator模式,和Chain of Responsibility模式类似。

Composite好处:
1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。

如何使用Composite?
首先定义一个接口或抽象类,这是设计模式通用方式了,其他设计模式对接口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Composite组合体的对象们(或称部件Component).

下面的代码是以抽象类定义,一般尽量用接口interface,

public abstract class Equipment
{
  private String name;
  //网络价格
  public abstract double netPrice();
  //折扣价格
  public abstract double discountPrice();
  //增加部件方法  
  public boolean add(Equipment equipment) { return false; }
  //删除部件方法
  public boolean remove(Equipment equipment) { return false; }
  //注意这里,这里就提供一种用于访问组合体类的部件方法。
  public Iterator iter() { return null; }
  
  public Equipment(final String name) { this.name=name; }
}

抽象类Equipment就是Component定义,代表着组合体类的对象们,Equipment中定义几个共同的方法。

public class Disk extends Equipment
{
  public Disk(String name) { super(name); }
  //定义Disk网络价格为1
  public double netPrice() { return 1.; }
  //定义了disk折扣价格是0.5 对折。
  public double discountPrice() { return .5; }
}

Disk是组合体内的一个对象,或称一个部件,这个部件是个单独元素( Primitive)。
还有一种可能是,一个部件也是一个组合体,就是说这个部件下面还有'儿子',这是树形结构中通常的情况,应该比较容易理解。现在我们先要定义这个组合体:

abstract class CompositeEquipment extends Equipment
{
  private int i=0;
  //定义一个Vector 用来存放'儿子'
  private Lsit equipment=new ArrayList();

  public CompositeEquipment(String name) { super(name); }

  public boolean add(Equipment equipment) {
     this.equipment.add(equipment);
     return true;
   }

  public double netPrice()
  {
    double netPrice=0.;
    Iterator iter=equipment.iterator();
    for(iter.hasNext())
      netPrice+=((Equipment)iter.next()).netPrice();
    return netPrice;
  }

  public double discountPrice()
  {
    double discountPrice=0.;
    Iterator iter=equipment.iterator();
    for(iter.hasNext())
      discountPrice+=((Equipment)iter.next()).discountPrice();
    return discountPrice;
  }
  

  //注意这里,这里就提供用于访问自己组合体内的部件方法。
  //上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元素.
  public Iterator iter()
  {
    return equipment.iterator() ;
  {
  //重载Iterator方法
   public boolean hasNext() { return i<equipment.size(); }
  //重载Iterator方法
   public Object next()
   {
    if(hasNext())
       return equipment.elementAt(i++);
    else
        throw new NoSuchElementException();
   }
  

}

上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实现.

我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子Cabinet,箱子里面可以放很多东西,如底板,电源盒,硬盘盒等;盘盒里面可以放一些小设备,如硬盘 软驱等。无疑这两个都是属于组合体性质的。

public class Chassis extends CompositeEquipment
{
   public Chassis(String name) { super(name); }
   public double netPrice() { return 1.+super.netPrice(); }
   public double discountPrice() { return .5+super.discountPrice(); }
}

public class Cabinet extends CompositeEquipment
{
   public Cabinet(String name) { super(name); }
   public double netPrice() { return 1.+super.netPrice(); }
   public double discountPrice() { return .5+super.discountPrice(); }
}

至此我们完成了整个Composite模式的架构。

我们可以看看客户端调用Composote代码:

Cabinet cabinet=new Cabinet("Tower");

Chassis chassis=new Chassis("PC Chassis");
//将PC Chassis装到Tower中 (将盘盒装到箱子里)
cabinet.add(chassis);
//将一个10GB的硬盘装到 PC Chassis (将硬盘装到盘盒里)
chassis.add(new Disk("10 GB"));

//调用 netPrice()方法;
System.out.println("netPrice="+cabinet.netPrice());
System.out.println("discountPrice="+cabinet.discountPrice());

上面调用的方法netPrice()或discountPrice(),实际上Composite使用Iteratbor遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行.

Composite是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。

以论坛为例,一个版(forum)中有很多帖子(message),这些帖子有原始贴,有对原始贴的回应贴,是个典型的树形结构,那么当然可以使用Composite模式,那么我们进入Jive中看看,是如何实现的.

Jive解剖
在Jive中 ForumThread是ForumMessages的容器container(组合体).也就是说,ForumThread类似我们上例中的 CompositeEquipment.它和messages的关系如图:
[thread]
   |- [message]
   |- [message]
      |- [message]
      |- [message]
         |- [message]

我们在ForumThread看到如下代码:

public interface ForumThread {
   ....
   public void addMessage(ForumMessage parentMessage, ForumMessage newMessage)
         throws UnauthorizedException;

   public void deleteMessage(ForumMessage message)
         throws UnauthorizedException;

  
   public Iterator messages();
      ....

}

类似CompositeEquipment, 提供用于访问自己组合体内的部件方法: 增加 删除 遍历.

结合我的其他模式中对Jive的分析,我们已经基本大体理解了Jive论坛体系的框架,如果你之前不理解设计模式,而直接去看Jive源代码,你肯定无法看懂。

:)

posted @ 2012-07-18 16:32 chen11-1 阅读(705) | 评论 (0)编辑 收藏

设计模式之Decorator(油漆工)

Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的.

Decorator定义:
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.

为什么使用Decorator?
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.

使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decoratbor提供了"即插即用"的方法,在运行期间决定何时增加何种功能.

如何使用?
举Adapter中的打桩示例,在Adapter中有两种类:方形桩 圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑 在桩上钉木板等,不关心如何使用两个不相关的类.

我们先建立一个接口:

public interface Work
{
  public void insert();

}

接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例:

public class SquarePeg implements Work{
  public void insert(){
    System.out.println("方形桩插入");
  }

}

现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻).

那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被刷油漆者),我们需要在decoratee上刷些"油漆",这些油漆就是那些额外的功能.

public class Decorator implements Work{

  private Work work;
  //额外增加的功能被打包在这个List中
  private ArrayList others = new ArrayList();

  //在构造器中使用组合new方式,引入Work对象;
  public Decorator(Work work)
  {
    this.work=work;
  
    others.add("挖坑");

    others.add("钉木板");
  }

  public void insert(){

    newMethod();
  }


  
  //在新方法中,我们在insert之前增加其他方法,这里次序先后是用户灵活指定的   
  public void newMethod()
  {
    otherMethod();
    work.insert();


  }

  public void otherMethod()
  {
    ListIterator listIterator = others.listIterator();
    while (listIterator.hasNext())
    {
      System.out.println(((String)(listIterator.next())) + " 正在进行");
    }

  }

}

在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排.

好了,Decorator模式出来了,我们看如何调用:

Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();

Decorator模式至此完成.

如果你细心,会发现,上面调用类似我们读取文件时的调用:

FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);

实际上Java 的I/O API就是使用Decorator实现的,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐.

Jive中的Decorator实现
在论坛系统中,有些特别的字是不能出现在论坛中如"打倒XXX",我们需要过滤这些"反动"的字体.不让他们出现或者高亮度显示.

IBM Java专栏中专门谈Jive的文章中,有谈及Jive中ForumMessageFilter.java使用了Decorator模式,其实,该程序并没有真正使用Decorator,而是提示说:针对特别论坛可以设计额外增加的过滤功能,那么就可以重组ForumMessageFilter作为Decorator模式了.

所以,我们在分辨是否真正是Decorator模式,以及会真正使用Decorator模式,一定要把握好Decorator模式的定义,以及其中参与的角色(Decoratee 和Decorator).

posted @ 2012-07-18 16:14 chen11-1 阅读(729) | 评论 (0)编辑 收藏

oracle blob 的读写 操作

--blob 的读写
CREATE OR REPLACE PROCEDURE P_IMG_INSERT (v_filename VARCHAR2)
IS
  v_bfile BFILE;
--文件指针
  v_blob BLOB;
  DIR CONSTANT 
VARCHAR2(20) := 'TEST';--文件存放DIRECTORY,区分大小写
   V_DEST NUMBER := 1;  
    V_LANG 
NUMBER := 1;  

BEGIN
  
/*通过empty_blob()函数将类型为blob的列初始化为空以便以后填充*/
  
INSERT INTO res_info (res_blob)
  
VALUES (EMPTY_BLOB ()) RETURN res_blob INTO v_blob;

  v_bfile:
= BFILENAME (DIR, v_filename);
 
  
IF (dbms_lob.fileexists(v_bfile)!=0THEN 
    dbms_lob.fileopen(v_bfile,dbms_lob.file_readonly); 
--打开目标文件
    /*将文件字数据加载到指定的LOB类型变量*/
    dbms_lob.loadblobfromfile(v_blob,
                                                      v_bfile,
                                                      dbms_lob.getlength(v_bfile),
                                                      V_DEST,
                              V_LANG);
     
-- dbms_lob.loadblobfromfile 
    dbms_lob.fileclose(v_bfile);--关闭文件
    COMMIT;
    dbms_output.put_line(
'已经从'||DIR||'目录中读取了文件'||v_filename||'向表中插入');

  
ELSE--如果文件定位器指向的文件不存在
    dbms_output.put_line('文件没找到');
  
END IF;
  EXCEPTION 
WHEN OTHERS THEN
  dbms_output.put_line(SQLERRM);
END;

说明下:
DBMS_LOB.LOADBLOBFROMFILE (
   dest_lob    IN OUT NOCOPY BLOB,
   src_bfile   IN            BFILE,
   amount      IN            INTEGER,
   dest_offset IN OUT        INTEGER,
   src_offset  IN OUT        INTEGER);

 

Parameter Description
dest_lob BLOB locator of the target for the load.
src_bfile BFILE locator of the source for the load.
amount Number of bytes to load from the BFILE. You can also use DBMS_LOB.LOBMAXSIZE to load until the end of the BFILE.
dest_offset (IN) Offset in bytbes in the destination BLOB (origin: 1) for the start of the write. (OUT) New offset in bytes in the destination BLOB right after the end of this write, which is also where the next write should begin.
src_offset (IN) Offset in bytes in the source BFILE (origin: 1) for the start of the read .(OUT) Offset in bytes in the source BFILE right after the end of this read, which is also where the next read should begin.

posted @ 2012-07-17 15:54 chen11-1 阅读(1489) | 评论 (0)编辑 收藏

Oracle 的Blob使用小结

Oracle 的Blob

Oracle的Lobs的流处理方式与Long等对象的Stream方式不一样,没有Long的诸多限制;只要保持连接,就能通过blob对象正确读取对象。
有两种方式可以读取Blob:
1.直接使用ps.getBinaryStream()的方法得到流对象
2.使用getBlob得到blob,然后通过blob的方法提供的getBinaryStream(),getBytes() 访问blob的数据。
这两种方法都可以在rs.close之后正确获取数据。(在spring 的JdbcTemplet环境下,该rs理论上被JdbcTemplet自动关闭;从数据库连接来看,连接也正确关闭了)。

使用Blob的好处是,按需获取Blob对象。而且可以多次通过blob.getBinaryStream得到对象。且Blob返回的对象可以使用mark/reset方法反复访问。且连接状态正常。
使用blob得到InputStream,可以调用close()接口,也可以不调用该接口,tb在连接关闭时将自动关闭该连接。最好调用close()释放资源。

c3p0的setBlob(pos,InputStream)接口不能正常工作。

写入或更新Blob时,可以使用ps.setBinaryStream();调用此接口后,in对象到文件尾(在把stream写入blob后,不能要再调用in.close()关闭文件,否则报错)。
也可以使用setBlob(pos,Blob)方法来写入或更新Blob字段;但是注意的是,无论是以blob还是blob.getBinaryStream的方式,都不能自己更新自己,否则死锁。

使用spring读取blob的示例程序:
            String sql = "select photo from my_photoes where id='test2' and photo is not null and rownum<2 ";
BLOB blob= (BLOB) simpleDao.queryForObject(sql,Blob.class);       
InputStream in = blob.getBinaryStream();
String filename = "./test/dao/pic" + 1+ ".gif";
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(filename));

            /* 需oracle的BLOB支持。效率可能会高些,但是空间上会有些浪费
byte[] b = new byte[blob.getBufferSize()];                //blob必须为oracle.sql.BLOB时才可调getBufferSize方法; 与java.sql.Blob区别。
System.out.println("bufferSize="+b.length);            //32k左右,用这种方式读取文件会有一点空间的浪费。
int len=-1;
while ((len = in.read(b)) != -1) {
out.write(b);
}
            */

            /*   纯jdbc方法:
                nt b;
     while ((b = in.read()) != -1) {
    out.write(b);
     }
            */

in.close();
out.close();



http://hi.baidu.com/hexiong/blog/item/34d7b2b7f9b3e8f431add103.html  其他参考
BLOB处理遇到的问题:
1.用spring的模板类来处理blob时,遇到大文件时,流会异常关闭。解决办法,使用oracle的本地连接来获取blob流,如下:
    public boolean queryForBlobStream(String sql,OutputStream fout)
    {
        boolean flag=true;
        try {
            Connection conn = DataSourceUtils.getConnection(getJdbcTemplate().getDataSource());
            conn.setAutoCommit(false);                        //此部分ms能提高性能
            Statement st = conn.createStatement();
            ResultSet rs = st.executeQuery(sql);
            if (rs.next()) {
                java.sql.Blob blob = rs.getBlob(1);
                InputStream ins = blob.getBinaryStream();
                //输出到文件
                //下面将BLOB数据写入文件
                byte[] b = new byte[1024];
                int len = 0;
                while ((len = ins.read(b)) != -1) {
                    fout.write(b, 0, len);
                }
                //依次关闭
                fout.close();
                ins.close();
            }
            conn.commit();
            rs.close();             //maybe not nessesary
            st.close();             //maybe not nessesary
            conn.close();
        } catch (IOException ex) {
            flag=false;
        } catch (SQLException ex) {
            flag=false;
        }
        return flag;
    }
2.如果把blob对象放到记录的字段中,在web开发中,通过blob.getBinaryStream()只能获得一次blob流,第二次调用同一对象的blob流会得到null流。
且在这种方式下,不能使用in.close()关闭流。

posted @ 2012-07-17 15:34 chen11-1 阅读(17817) | 评论 (0)编辑 收藏

  oracle 字段类型

 oracle 字段类型   CHAR    固定长度字符串    最大长度2000    bytes           

   VARCHAR2    可变长度的字符串    最大长度4000    bytes      可做索引的最大长度749     

   NCHAR    根据字符集而定的固定长度字符串    最大长度2000    bytes           

   NVARCHAR2    根据字符集而定的可变长度字符串    最大长度4000    bytes           

   DATE    日期(日-月-年)    DD-MM-YY(HH-MI-SS)    经过严格测试,无千虫问题     

   LONG    超长字符串    最大长度2G(231-1)    足够存储大部头著作     

   RAW    固定长度的二进制数据    最大长度2000    bytes      可存放多媒体图象声音等     

   LONG    RAW    可变长度的二进制数据    最大长度2G    同上     

   BLOB    二进制数据    最大长度4G         

   CLOB    字符数据    最大长度4G         

   NCLOB    根据字符集而定的字符数据    最大长度4G         

   BFILE    存放在数据库外的二进制数据    最大长度4G         

   ROWID    数据表中记录的唯一行号    10    bytes    ********.****.****格式,*为0或1     

   NROWID    二进制数据表中记录的唯一行号    最大长度4000    bytes     

   NUMBER(P,S)    数字类型    P为整数位,S为小数位     

   DECIMAL(P,S)    数字类型    P为整数位,S为小数位     

   INTEGER    整数类型    小的整数     

   FLOAT    浮点数类型    NUMBER(38),双精度     

   REAL    实数类型    NUMBER(63),精度更高     

数据类型 参数 描述

char(n) n=1 to 2000字节 定长字符串,n字节长,如果不指定长度,缺省为1个字节长(一个汉字为2字节)

varchar2(n) n=1 to 4000字节 可变长的字符串,具体定义时指明最大长度n,

这种数据类型可以放数字、字母以及ASCII码字符集(或者EBCDIC等数据库系统接受的字符集标准)中的所有符号。

如果数据长度没有达到最大值n,Oracle 8i会根据数据大小自动调节字段长度,

如果你的数据前后有空格,Oracle 8i会自动将其删去。VARCHAR2是最常用的数据类型。

可做索引的最大长度3209。

number(m,n) m=1 to 38

n=-84 to 127 可变长的数值列,允许0、正值及负值,m是所有有效数字的位数,n是小数点以后的位数。

如:number(5,2),则这个字段的最大值是99,999,如果数值超出了位数限制就会被截取多余的位数。

如:number(5,2),但在一行数据中的这个字段输入575.316,则真正保存到字段中的数值是575.32。

如:number(3,0),输入575.316,真正保存的数据是575。  

date 无 从公元前4712年1月1日到公元4712年12月31日的所有合法日期,

Oracle 8i其实在内部是按7个字节来保存日期数据,在定义中还包括小时、分、秒。

缺省格式为DD-MON-YY,如07-11月-00 表示2000年11月7日。  

long 无 可变长字符列,最大长度限制是2GB,用于不需要作字符串搜索的长串数据,如果要进行字符搜索就要用varchar2类型。

long是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

raw(n) n=1 to 2000 可变长二进制数据,在具体定义字段的时候必须指明最大长度n,Oracle 8i用这种格式来保存较小的图形文件或带格式的文本文件,如Miceosoft Word文档。

raw是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

long raw 无 可变长二进制数据,最大长度是2GB。Oracle 8i用这种格式来保存较大的图形文件或带格式的文本文件,如Miceosoft Word文档,以及音频、视频等非文本文件。

在同一张表中不能同时有long类型和long raw类型,long raw也是一种较老的数据类型,将来会逐渐被BLOB、CLOB、NCLOB等大的对象数据类型所取代。  

blob

clob

nclob 无 三种大型对象(LOB),用来保存较大的图形文件或带格式的文本文件,如Miceosoft Word文档,以及音频、视频等非文本文件,最大长度是4GB。

LOB有几种类型,取决于你使用的字节的类型,Oracle 8i实实在在地将这些数据存储在数据库内部保存。

可以执行读取、存储、写入等特殊操作。  

bfile 无 在数据库外部保存的大型二进制对象文件,最大长度是4GB。

这种外部的LOB类型,通过数据库记录变化情况,但是数据的具体保存是在数据库外部进行的。

Oracle 8i可以读取、查询BFILE,但是不能写入。

大小由操作系统决定。  

数据类型是列或存储过程中的一个属性。

    Oracle支持的数据类型可以分为三个基本种类:字符数据类型、数字数据类型以及表示其它数据的数据类型。

    字符数据类型

      CHAR             char数据类型存储固定长度的字符值。一个CHAR数据类型可以包括1到2000个字符。如果对CHAR没有明确地说明长度,它的默认长度则设置为1。如果对某个CHAR类型变量赋值,其长度小于规定的长度,那么Oracle自动用空格填充。

      VARCHAR2 存储可变长度的字符串。虽然也必须指定一个VARCHAR2数据变量的长度,但是这个长度是指对该变量赋值的最大长度而非实际赋值长度。不需用空格填充。最多可设置为4000个字符。因为VARCHAR2数据类型只存储为该列所赋的字符(不加空格),所以VARCHAR2需要的存储空间比CHAR数据类型要小。

    Oracle推荐使用VARCHAR2

      NCHAR和NVARCHAR2 NCHAR和NVARCHAR2数据类型分别存储固定长度与可变长度的字符串,但是它们使用的是和数据库其他类型不同的字符集。在创建数据库时,需要指定所使用的字符集,以便对数据中数据进行编码。还可以指定一个辅助的字符集[即本地语言集]。NCHAR和NVARCHAR2类型的列使用辅助字符集。NCHAR和NVARCHAR2类型的列使用辅助字符集。

      在Oracle 9i中,可以以字符而不是字节为单位表示NCHAR和NVARCHAR2列的长度。

      LONG long数据类型可以存放2GB的字符数据,它是从早期版本中继承下来的。现在如果存储大容量的数据,Oracle推荐使用CLOB和NCLOB数据类型。在表和SQL语句中使用LONG类型有许多限制。

      CLOB和NCLOB    CLOB和NCLOB数据类型可以存储多达4GB的字符数据。NCLOB数据类型可存储NLS数据。

      数字数据类型

      Oracle使用标准、可变长度的内部格式来存储数字。这个内部格式精度可以高达38位。

      NUMBER数据类型可以有两个限定符,如:column NUMBER(precision,scale)。precision表示数字中的有效位。如果没有指定precision的话,Oracle将使用38作为精度。scale表示小数点右边的位数,scale默认设置为0。如果把scale设成负数,Oracle将把该数字取舍到小数点左边的指定位数。

      日期数据类型

       Oracle标准日期格式为:DD-MON-YY HH:MI:SS

       通过修改实例的参数NLS_DATE_FORMAT,可以改变实例中插入日期的格式。在一个会话期间,可以通过Alter session SQL命令来修改日期,或者通过使用SQL语句的TO_DATE表达式中的参数来更新一个特定值。

       其它的数据类型

       RAW和LONG RAW    RAW和LONG RAW数据类型主要用于对数据库进行解释。tb指定这两种类型时,Oracle以位的形式来存储数据。RAW数据类型一般用于存储有特定格式的对象,如位图。RAW数据类型可占用2KB的空间,而LONG RAW数据类型则可以占用2GB大小。

       ROWID ROWID是一种特殊的列类型,称之为伪列(pseudocolumn)。ROWID伪列在SQL SELECT语句中可以像普通列那样被访问。Oracle数据库中每行都有一个伪列。ROWID表示行的地址,ROWID伪列用ROWID数据类型定义。

       ROWID与磁盘驱动的特定位置有关,因此,ROWID是获得行的最快方法。但是,行的ROWID会随着卸载和重载数据库而发生变化,因此建议不要在事务中使用ROWID伪列的值。例如,一旦当前应用已经使用完记录,就没有理由保存行的ROWID。不能通过任何SQL语句来设置标准的ROWID伪列的值。

      列或变量可以定义成ROWID数据类型,但是Oracle不能保证该列或变量的值是一个有效的ROWID。

    LOB(大型对象)数据类型,可以保存4GB的信息。LOB有以下3中类型:

      <CLOB>,只能存储字符数据

    <NCLOB>,保存本地语言字符集数据

    <BLOB>    ,以二进制信息保存数据

      可以指定将一个LOB数据保存在Oracle数据库内,还是指向一个包含次数据的外部文件。

      LOB可以参与事务。管理LOB中的数据必须通过DBMS_LOB PL/SQL内置软件包或者OGI接口。

      为了便于将LONG数据类型转换成LOB,Oracle 9i包含许多同时支持LOB和LONG的函数,喊包括一个ALTER TABLE语句的新选择,它允许将LONG数据类型自动转换成LOB。

    BFILE

      BFILE数据类型用做指向存储在Oracle数据库以外的文件的指针。

      XML Type

      作为对XML支持的一部分,Oracle 9i包含了一个新的数据类型XML Type。定义为XMLType的列将存储一个字符LOB列中的XML文档。有许多内置的功能可以使你从文档中抽取单个节点,还可以在XML Type文档中对任何节点创建索引。

      用户自定义数据

      从Oracle 8以后,用户可以定义自己的复杂数据类型,它们由Oracle基本数据类型组合而成。

      AnyType、AnyData和AnyDataSet

       Oracle包括3个新的数据类型,用于定义在现有数据类型之外的数据结构。其中每种数据类型必须用程序单元来定义,以便让Oracle9i知道如何处理这些类型的特定实现。

    类型转换

    Oracle会自动将某些数据类型转换成其他的数据类型,转换取决于包括该值的SQL语句。

    数据转换还可以通过Oracle的类型转换函数显示地进行。

    连接与比较

    在大多数平台上Oracle SQL中的连接操作符用两条竖线(||)表示。连接是将两个字符值连接。Oracle的自动类型转换功能使得两个数字值也可以进行连接。

    NULL

    NULL值是关系数据库的重要特征之一。实际上,NULL不代表任何值,它表示没有值。如果要创建表的一个列,而这个列必须有值,那么应将它指定为NOT NULL,这表示该列不能包含NULL值。

    任何数据类型都可以赋予NULL值。NULL值引入了SQL运算的三态逻辑。如果比较的一方是NULL值,那么会出现3种状态:TURE、FALSE以及两者都不是。

    因为NULL值不等于0或其他任何值,所以测试某个数据是否为NULL值只能通过关系运算符IS NULL来进行。

    NULL值特别适合以下情况:当一个列还未赋值时。如果选择不使用NULL值,那么必须对行的所有列都要赋值。这实际上也取消了某列不需要值的可能性,同时对它赋的值也很容易产生误解。这种情况则可能误导终端用户,并且导致累计操作的错误结果。

number(p,s)

p:1~38

s:-84~127

p>0,对s分2种情况:1. s>0

精确到小数点右边s位,并四舍五入。然后检验有效数位是否<=p;如果s>p,小数点右边至少有s-p个0填充。

2. s<0

精确到小数点左边s位,并四舍五入。然后检验有效数位是否<=p+|s|

123.2564 NUMBER 123.2564

1234.9876 NUMBER(6,2) 1234.99

12345.12345 NUMBER(6,2) Error

1234.9876 NUMBER(6) 1235

12345.345 NUMBER(5,-2) 12300

1234567 NUMBER(5,-2) 1234600

12345678 NUMBER(5,-2) Error

123456789 NUMBER(5,-4) 123460000

1234567890 NUMBER(5,-4) Error

12345.58 NUMBER(*, 1) 12345.6

0.1 NUMBER(4,5) Error

0.01234567 NUMBER(4,5) 0.01235

0.09999 NUMBER(4,5) 0.09999

posted @ 2012-07-17 15:20 chen11-1 阅读(1041) | 评论 (0)编辑 收藏

Java编程基础-变量,常量,运算符,函数,程序流程控制

今天看了第二章Java编程基础,总体来说这一章我自认为是最简单的一张。同其他的编程语言一样,同样有变量,常量,运算符,函数,程序流程控制等。但是我觉得学好这一章主要是要抓住Java与其他的语言的不同,至于相同的就没必要花费大量的心思去研究了。

首先Java是严格区分大小写的,我觉得这正是语言的严谨性的一个重要方便,很多不错的编程语言都区分,如C,C++,C#,PHP等。Java的格式是自由的多个语句可以写在一行,一个语句也可以写在多行,但是一个连续的字符串不能分开在在多行写,功能执行语句必须以;结束。为了使程序具有可读性,还是要按照缩进和对齐的标准来写。

Java的注释有三种,前两种是其他语言所共有的,而文档注释是Java所特有的。文档注释是以结束。Javadoc工具是处理文档注释的工具,Javadoc可以将文档注释的内容信息取出,然后转换为HTML的格式文档。如:

Javadoc运行程序的命令格式为javadoc –d 类名 –version –author 源文件名。

Java中标示符的规定是:任意顺序的大小写字母,数字,下划线,tb美元符号,但不能以数字开头,最主要的特点可以包含美元符号。Java中的常量包括:整型,浮点型,布尔型,字符型,字符串型。内建有8种基本变量:整型(byte,short,int,long)浮点型(floatdouble)字符型(char)布尔型(boolean)。数据类型之间的转换包括自动类型的转换和强制类型转换。实现自动类型的转换符合的条件是两种类型彼此兼容和目标类型的取值范围大于源类型。如byte可以自动转换为short,int,long。不会产生数据丢失。而强制类型转换一般会有数据的丢失。格式为 目标类型 目标变量=(目标类型)值。

表达式的类型提升规则:

1. 所有的byte,short,char类型可以自动提升为int

2. 如果一个操作数是long型,计算结果就是long

3. 如果一个操作数是float型,计算结果就是float

4. 如果一个操作数是double型,计算结果就是double

关于函数和函数的重载问题,函数的概念就不用提了,是所有编程语言所共有的。关于函数重载是面向对象的编程语言所特有的。在一个类中允许有一个以上的同名函数,只要参数个数或类型不同即可,在这种情况下,就叫做重载。Java中的运算符和C语言的运算符基本相同,没有什么异同。

程序的流程控制有三种结构:顺序结构,循环结构,选择结构。这三个结构没什么特殊的,和其他的语言没有什么不同。break语句是可以中止循环体内的语句和switch语句而continue语句就是跳出当前循环的剩余语句块。

Java中没有真正的多维数组,只有数组的数组。Java中的多维数组不一定是规则矩阵的形式。一些与数组操作相关的函数:system.arraycopy()函数用于复制数组,Arrays.sort()函数是用来排序数组。

posted @ 2012-07-16 13:26 chen11-1 阅读(1041) | 评论 (0)编辑 收藏

设计模式之Flyweight(享元)

设计模式之Flyweight(享元)

Flyweight定义:
避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).

为什么使用?
面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个对象,几千个字,对象数就是几千,无疑耗费内存,那么我们还是要"求同存异",找出这些对象群的共同点,设计一个元类,封装可以被共享的类,另外,还有一些特性是取决于应用(context),是不可共享的,这也Flyweight中两个重要概念内部状态intrinsic和外部状态extrinsic之分.

说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式.Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象.

Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight池(pool)中.

如何使用?

我们先从Flyweight抽象接口开始:

public interface Flyweightb
{
  public void operation( ExtrinsicState state );
}

//用于本模式的抽象数据类型(自行设计)
public interface ExtrinsicState { }

下面是接口的具体实现(ConcreteFlyweight) ,并为内部状态增加内存空间, ConcreteFlyweight必须是可共享的,它保存的任何状态都必须是内部(intrinsic),也就是说,ConcreteFlyweight必须和它的应用环境场合无关.

public class ConcreteFlyweight implements Flyweight {
  private IntrinsicState state;
  
  public void operation( ExtrinsicState state )
  {
      //具体操作
  }

}

当然,并不是所有的Flyweight具体实现子类都需要被共享的,所以还有另外一种不共享的ConcreteFlyweight:

public class UnsharedConcreteFlyweight implements Flyweight {

  public void operation( ExtrinsicState state ) { }

}

Flyweight factory负责维护一个Flyweight池(存放内部状态),当客户端请求一个共享Flyweight时,这个factory首先搜索池中是否已经有可适用的,如果有,factory只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池

public class FlyweightFactory {
  //Flyweight pool
  private Hashtable flyweights = new Hashtable();

  public Flyweight getFlyweight( Object key ) {

    Flyweight flyweight = (Flyweight) flyweights.get(key);

    if( flyweight == null ) {
      //产生新的ConcreteFlyweight
      flyweight = new ConcreteFlyweight();
      flyweights.put( key, flyweight );
    }

    return flyweight;
  }
}

至此,Flyweight模式的基本框架已经就绪,我们看看如何调用:

FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
......

从调用上看,好象是个纯粹的Factory使用,但奥妙就在于Factory的内部设计上.

Flyweight模式在XML等数据源中应用
我们上面已经提到,当大量从数据源中读取字符串,其中肯定有重复的,那么我们使用Flyweight模式可以提高效率,以唱片CD为例,在一个XML文件中,存放了多个CD的资料.

每个CD有三个字段:
1.出片日期(year)
2.歌唱者姓名等信息(artist)
3.唱片曲目 (title)

其中,歌唱者姓名有可能重复,也就是说,可能有同一个演唱者的多个不同时期 不同曲目的CD.我们将"歌唱者姓名"作为可共享的ConcreteFlyweight.其他两个字段作为UnsharedConcreteFlyweight.

首先看看数据源XML文件的内容:


<?xml version="1.0"?>
<collection>

<cd>
<title>Another Green World</title>
<year>1978</year>
<artist>Eno, Brian</artist>
</cd>

<cd>
<title>Greatest Hits</title>
<year>1950</year>
<artist>Holiday, Billie</artist>
</cd>

<cd>
<title>Taking Tiger Mountain (by strategy)</title>
<year>1977</year>
<artist>Eno, Brian</artist>
</cd>

.......

</collection>


虽然上面举例CD只有3张,CD可看成是大量重复的小类,因为其中成分只有三个字段,而且有重复的(歌唱者姓名).

CD就是类似上面接口 Flyweight:


public class CD {

  private String title;
  private int year;
  private Artist artist;

  public String getTitle() {  return title; }
  public int getYear() {    return year;  }
  public Artist getArtist() {    return artist;  }

  public void setTitle(String t){    title = t;}
  public void setYear(int y){year = y;}
  public void setArtist(Artist a){artist = a;}

}

"歌唱者姓名"作为可共享的ConcreteFlyweight:

public class Artist {

  //内部状态
  private String name;

  // note that Artist is immutable.
  String getName(){return name;}

  Artist(String n){
    name = n;
  }

}

再看看Flyweight factory,专门用来制造上面的可共享的ConcreteFlyweight:Artist

public class ArtistFactory {

  Hashtable pool = new Hashtable();

  Artist getArtist(String key){

    Artist result;
    result = (Artist)pool.get(key);
    ////产生新的Artist
    if(result == null) {
      result = new Artist(key);
      pool.put(key,result);
      
    }
    return result;
  }

}

当你有几千张甚至更多CD时,Flyweight模式将节省更多空间,共享的flyweight越多,空间节省也就越大.

posted @ 2012-07-16 13:25 chen11-1 阅读(864) | 评论 (0)编辑 收藏

设计模式之Bridge

设计模式之Bridge
板桥里人 http://www.jdon.com 2002/05/01

Bridge定义 :
将抽象和行为划分开来,各自独立,但能动态的结合.

为什么使用?
通常,当一个抽象类或接口有多个具体实现(concrete subclass),这些concrete之间关系可能有以下两种:
1. 这多个具体实现之间恰好是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复,那么我们只要使用继承就可以了.

2.实际应用上,常常有可能在这多个concrete class之间有概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象和行为.

例如,一杯咖啡为例,有中杯和大杯之分,同时还有加奶 不加奶之分. 如果用单纯的继承,这四个具体实现(中杯 大杯 加奶 不加奶)之间有概念重叠,因为有中杯加奶,也有中杯不加奶, 如果再在中杯这一层再实现两个继承,很显然混乱,扩展性极差.那我们使用Bridge模式来实现它.

如何实现?
以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.

先看看抽象部分的接口代码:

public abstract class Coffee
{
  CoffeeImp coffeeImp;

  public void setCoffeeImp() {
    this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
  }

  public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

  public abstract void pourCoffee();
}
 

其中CoffeeImp 是加不加奶的行为接口,看其代码如下:

public abstract class CoffeeImp
{
  public abstract void pourCoffeeImp();
}
 

现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class:

//中杯
public class MediumCoffee extends Coffee
{
  public MediumCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
    for (int i = 0; i < 2; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}

//大杯
public class SuperSizeCoffee extends Coffee
{
  public SuperSizeCoffee() {setCoffeeImp();}

  public void pourCoffee()
  {
    CoffeeImp coffeeImp = this.getCoffeeImp();
    //我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
    for (int i = 0; i < 5; i++)
    {

      coffeeImp.pourCoffeeImp();
    }
  
  }
}
 

上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:

//加奶
public class MilkCoffeeImp extends CoffeeImp
{
  MilkCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("加了美味的牛奶");
  }
}

//不加奶
public class FragrantCoffeeImp extends CoffeeImp
{
  FragrantCoffeeImp() {}

  public void pourCoffeeImp()
  {
    System.out.println("什么也没加,清香");
  }
}
 

Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶

看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:

public class CoffeeImpSingleton
{
  private static CoffeeImp coffeeImp;

  public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
   {this.coffeeImp = coffeeImpIn;}

  public static CoffeeImp getTheCoffeeImp()
  {
    return coffeeImp;
  }
}
 

看看中杯加奶 和大杯加奶 是怎么出来的:

//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee();
mediumCoffee.pourCoffee();

//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
superSizeCoffee.pourCoffee();

注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键,

Bridge模式在EJB中的应用
EJB中有一个Data Access Object (DAO)模式,这是将商业逻辑和具体数据资源分开的,因为不同的数据库有不同的数据库操作.将操作不同数据库的行为独立抽象成一个行为接口DAO.如下:

1.Business Object (类似Coffee)

实现一些抽象的商业操作:如寻找一个用户下所有的订单

涉及数据库操作都使用DAOImplementbor.

2.Data Access Object (类似CoffeeImp)

一些抽象的对数据库资源操作
3.DAOImplementor 如OrderDAOCS, OrderDAOOracle, OrderDAOSybase(类似MilkCoffeeImp FragrantCoffeeImp)

具体的数据库操作,如"INSERT INTO "等语句,OrderDAOOracle是Oracle OrderDAOSybase是Sybase数据库.

4.数据库 (Cloudscape, Oracle, or Sybase database via JDBC API)

 

posted @ 2012-07-16 13:24 chen11-1 阅读(878) | 评论 (0)编辑 收藏

配置Apache+Tomcat集群

操作系统:Debian6.0 (192.168.225.129 虚拟机1台)

软件版本:Apache-2.2.16, Tomcat-6.0.35

1.安装软件

    >apt-get install update

    *安装Apache

    >apt-get install apache2

    *安装Tomcat

    >wget http://apache.etoak.com/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz

    >tar zxvf apache-tomcat-6.0.35.tar.gz

    >mv apache-tomcat-6.0.35 /user/local/tomcat

    >cp /usr/local/tomcat  /usr/local/tomcat2

    *安装mod_jk

    >apt-get install libapache2-mod-jk

2.配置2个Tomcat

    现在/usr/local目录中已经有2个tomcat目录了,需要更改tomcat2的端口,防止端口冲突。

    >nano /usr/local/tomcat/conf/server.xml

    有3处的默认端口需要更改:

    1.<Server port="8004" shutdown="SHUTDOWN"> 我更改为8003

    2.<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" />

        更改为7080

    3.<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

        更改为7009,其中redirectPort 8443不需要更改

    o.另外还需要在<Engine>配置中加入<Clustber>配置

 

Java代码 复制代码 收藏代码
  1. <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"  
  2.     channelSendOptions="8">   
  3.     <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false"    
  4.         notifyListenersOnReplication="true"/>      
  5.     <Channel className="org.apache.catalina.tribes.group.GroupChannel">     
  6.         <Membership className="org.apache.catalina.tribes.membership.McastService"    
  7.             address="228.0.0.4"    
  8.             port="45564"    
  9.             frequency="500"    
  10.             dropTime="3000"/>     
  11.         <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"    
  12.             address="auto"    
  13.             port="4001"    
  14.             selectorTimeout="5000"    
  15.             maxThreads="6"/>     
  16.         <!-- timeout="60000"-->     
  17.         <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">     
  18.             <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>     
  19.         </Sender>     
  20.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>     
  21.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>     
  22.         <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>     
  23.     </Channel>    
  24.     <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>     
  25.     <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>      
  26.     <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"    
  27.         tempDir="/tmp/war-temp/"    
  28.         deployDir="/tmp/war-deploy/"    
  29.         watchDir="/tmp/war-listen/"    
  30.         watchEnabled="false"/>    
  31.     <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>     
  32.     <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>       
  33. </Cluster>  

     x.最后2个tomcat都需要改变<Engine的 jvmRoute属性分别为tomcat1和tomcat2,以对应之后worker.properties中的名字。


3.配置Apache以jk方式和tomcat集群

   安装完成后apache的主目录为/etc/apache2,安装完mod-jk之后,mods-enabled里面会多一个jk.load

   创建文件   /etc/apache2/mods-enabled/jk.conf

    >nano /etc/apache2/mods-enabled/jk.conf

       JkWorkersFile /etc/apache2/workers.properties
       JkShmFile     /var/log/apache2/mod_jk.shm
       JkLogFile     /var/log/apache2/mod_jk.log
       JkLogLevel    info

   创建文件  /etc/apache2/workers.properties

    >nano /etc/apache2/workers.properties

       worker.list=controller1

       worker.tomcat1.port=8009
       worker.tomcat1.host=localhost
       worker.tomcat1.type=ajp13
       worker.tomcat1.lbfactor=1

       worker.tomcat2.port=7009
       worker.tomcat2.host=localhost
       worker.tomcat2.type=ajp13
       worker.tomcat2.lbfactor=1

       worker.controller1.type=lb
       worker.controller1.sticky_session=1   #可选项0,1
       worker.controller1.balance_workers=tomcat1,tomcat2

    更改/etc/apache2/sites-enabled/000-default文件

      在</VirtualHost>之前添加

       JkMount /* controller1

       (controller1对应workers.properties中的名字)

配置完成之后先后启动tomcat和apache。

测试:

2个tomcat部署相同的应用:即首页index.jsp显示各自tomcat名称tomcat1和tomcat2

通过80端口访问应用,刷新几次,tomcat1和tomcat2轮流显示。

posted @ 2012-07-13 13:53 chen11-1 阅读(1455) | 评论 (0)编辑 收藏

SSL介绍与Java实例

有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多。但如果我们不能够亲自动手做一个SSL Sever和SSL Client,可能就永远也不能深入地理解Java环境下,SSL的通信是如何实现的。对SSL中的各种概念的认识也可能会仅限于可以使用的程度。本文通过构造一个简单的SSL Server和SSL Client来讲解Java环境下SSL的通信原理。

首先我们先回顾一下常规的Java Socket编程。在Java下写一个Socket服务器和客户端的例子还是比较简单的。以下是服务端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.IOException;  
5.import java.io.InputStbreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.ServerSocket;  
8.import java.net.Socket;  
9. 
10.public class Server extends Thread {  
11.    private Socket socket;  
12. 
13.    public Server(Socket socket) {  
14.        this.socket = socket;  
15.    }  
16. 
17.    public void run() {  
18.        try {  
19.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
20.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
21. 
22.            String data = reader.readLine();  
23.            writer.println(data);  
24.            writer.close();  
25.            socket.close();  
26.        } catch (IOException e) {  
27. 
28.        }  
29.    }  
30.      
31.    public static void main(String[] args) throws Exception {  
32.        while (true) {  
33.            new Server((new ServerSocket(8080)).accept()).start();  
34.        }  
35.    }  
36.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends Thread {
 private Socket socket;

 public Server(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }
 
 public static void main(String[] args) throws Exception {
  while (true) {
   new Server((new ServerSocket(8080)).accept()).start();
  }
 }
}


服务端很简单:侦听8080端口,并把客户端发来的字符串返回去。下面是客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.public class Client {  
9. 
10.    public static void main(String[] args) throws Exception {  
11. 
12.        Socket s = new Socket("localhost", 8080);  
13. 
14.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
15.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
16.        writer.println("hello");  
17.        writer.flush();  
18.        System.out.println(reader.readLine());  
19.        s.close();  
20.    }  
21. 
22.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {

 public static void main(String[] args) throws Exception {

  Socket s = new Socket("localhost", 8080);

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

}


客户端也非常简单:向服务端发起请求,发送一个"hello"字串,然后获得服务端的返回。把服务端运行起来后,执行客户端,我们将得到"hello"的返回。

就是这样一套简单的网络通信的代码,我们来把它改造成使用SSL通信。在SSL通信协议中,我们都知道首先服务端必须有一个数字证书,当客户端连接到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。

因此,我们首先要为服务端生成一个数字证书。Java环境下,数字证书是用keytool生成的,这些证书被存储在store的概念中,就是证书仓库。我们来调用keytool命令为服务端生成数字证书和保存它使用的证书仓库:


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123 
keytool -genkey -v -alias bluedash-ssl-demo-server -keyalg RSA -keystore ./server_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass server -keypass 123123


这样,我们就将服务端证书bluedash-ssl-demo-server保存在了server_ksy这个store文件当中。有关keytool的用法在本文中就不再多赘述。执行上面的命令得到如下结果:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./server_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./server_ks]


然后,改造我们的服务端代码,让服务端使用这个证书,并提供SSL通信:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.IOException;  
6.import java.io.InputStreamReader;  
7.import java.io.PrintWriter;  
8.import java.net.ServerSocket;  
9.import java.net.Socket;  
10.import java.security.KeyStore;  
11. 
12.import javax.net.ServerSocketFactory;  
13.import javax.net.ssl.KeyManagerFactory;  
14.import javax.net.ssl.SSLContext;  
15.import javax.net.ssl.SSLServerSocket;  
16. 
17.public class SSLServer extends Thread {  
18.    private Socket socket;  
19. 
20.    public SSLServer(Socket socket) {  
21.        this.socket = socket;  
22.    }  
23. 
24.    public void run() {  
25.        try {  
26.            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));  
27.            PrintWriter writer = new PrintWriter(socket.getOutputStream());  
28. 
29.            String data = reader.readLine();  
30.            writer.println(data);  
31.            writer.close();  
32.            socket.close();  
33.        } catch (IOException e) {  
34. 
35.        }  
36.    }  
37. 
38.    private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";  
39.    private static String SERVER_KEY_STORE_PASSWORD = "123123";  
40. 
41.    public static void main(String[] args) throws Exception {  
42.        System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);  
43.        SSLContext context = SSLContext.getInstance("TLS");  
44.          
45.        KeyStore ks = KeyStore.getInstance("jceks");  
46.        ks.load(new FileInputStream(SERVER_KEY_STORE), null);  
47.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
48.        kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());  
49.          
50.        context.init(kf.getKeyManagers(), null, null);  
51. 
52.        ServerSocketFactory factory = context.getServerSocketFactory();  
53.        ServerSocket _socket = factory.createServerSocket(8443);  
54.        ((SSLServerSocket) _socket).setNeedClientAuth(false);  
55. 
56.        while (true) {  
57.            new SSLServer(_socket.accept()).start();  
58.        }  
59.    }  
60.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;

public class SSLServer extends Thread {
 private Socket socket;

 public SSLServer(Socket socket) {
  this.socket = socket;
 }

 public void run() {
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   PrintWriter writer = new PrintWriter(socket.getOutputStream());

   String data = reader.readLine();
   writer.println(data);
   writer.close();
   socket.close();
  } catch (IOException e) {

  }
 }

 private static String SERVER_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/server_ks";
 private static String SERVER_KEY_STORE_PASSWORD = "123123";

 public static void main(String[] args) throws Exception {
  System.setProperty("javax.net.ssl.trustStore", SERVER_KEY_STORE);
  SSLContext context = SSLContext.getInstance("TLS");
  
  KeyStore ks = KeyStore.getInstance("jceks");
  ks.load(new FileInputStream(SERVER_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
  
  context.init(kf.getKeyManagers(), null, null);

  ServerSocketFactory factory = context.getServerSocketFactory();
  ServerSocket _socket = factory.createServerSocket(8443);
  ((SSLServerSocket) _socket).setNeedClientAuth(false);

  while (true) {
   new SSLServer(_socket.accept()).start();
  }
 }
}


可以看到,服务端的Socket准备设置工作大大增加了,增加的代码的作用主要是将证书导入并进行使用。此外,所使用的Socket变成了SSLServerSocket,另外端口改到了8443(这个不是强制的,仅仅是为了遵守习惯)。另外,最重要的一点,服务端证书里面的CN一定和服务端的域名统一,我们的证书服务的域名是localhost,那么我们的客户端在连接服务端时一定也要用localhost来连接,否则根据SSL协议标准,域名与证书的CN不匹配,说明这个证书是不安全的,通信将无法正常运行。

有了服务端,我们原来的客户端就不能使用了,必须要走SSL协议。由于服务端的证书是我们自己生成的,没有任何受信任机构的签名,所以客户端是无法验证服务端证书的有效性的,通信必然会失败。所以我们需要为客户端创建一个保存所有信任证书的仓库,然后把服务端证书导进这个仓库。这样,当客户端连接服务端时,会发现服务端的证书在自己的信任列表中,就可以正常通信了。

因此现在我们要做的是生成一个客户端的证书仓库,因为keytool不能仅生成一个空白仓库,所以和服务端一样,我们还是生成一个证书加一个仓库(客户端证书加仓库):


Bash代码 
1.keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456 
keytool -genkey -v -alias bluedash-ssl-demo-client -keyalg RSA -keystore ./client_ks -dname "CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn" -storepass client -keypass 456456


结果如下:


Bash代码 
1.Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days  
2.        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.[Storing ./client_ks] 
Generating 1,024 bit RSA key pair and self-signed certificate (SHA1withRSA) with a validity of 90 days
        for: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
[Storing ./client_ks]

接下来,我们要把服务端的证书导出来,并导入到客户端的仓库。第一步是导出服务端的证书:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer 
keytool -export -alias bluedash-ssl-demo-server -keystore ./server_ks -file server_key.cer

执行结果如下:


Bash代码 
1.Enter keystore password:  server  
2.Certificate stored in file <server_key.cer> 
Enter keystore password:  server
Certificate stored in file <server_key.cer>

然后是把导出的证书导入到客户端证书仓库:


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks 
keytool -import -trustcacerts -alias bluedash-ssl-demo-server -file ./server_key.cer -keystore ./client_ks

结果如下:


Bash代码 
1.Enter keystore password:  client  
2.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
3.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Serial number: 4c57c7de  
5.Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010 
6.Certificate fingerprints:  
7.         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C  
8.         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4  
9.         Signature algorithm name: SHA1withRSA  
10.         Version: 3 
11.Trust this certificate? [no]:  yes  
12.Certificate was added to keystore 
Enter keystore password:  client
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c7de
Valid from: Tue Aug 03 15:40:14 CST 2010 until: Mon Nov 01 15:40:14 CST 2010
Certificate fingerprints:
         MD5:  FC:D4:8B:36:3F:1B:30:EA:6D:63:55:4F:C7:68:3B:0C
         SHA1: E1:54:2F:7C:1A:50:F5:74:AA:63:1E:F9:CC:B1:1C:73:AA:34:8A:C4
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

好,准备工作做完了,我们来撰写客户端的代码:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.InputStreamReader;  
5.import java.io.PrintWriter;  
6.import java.net.Socket;  
7. 
8.import javax.net.SocketFactory;  
9.import javax.net.ssl.SSLSocketFactory;  
10. 
11.public class SSLClient {  
12. 
13.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
14. 
15.    public static void main(String[] args) throws Exception {  
16.        // Set the key store to use for validating the server cert.  
17.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
18.          
19.        System.setProperty("javax.net.debug", "ssl,handshake");  
20. 
21.        SSLClient client = new SSLClient();  
22.        Socket s = client.clientWithoutCert();  
23. 
24.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
25.        BufferedReader reader = new BufferedReader(new InputStreamReader(s  
26.                .getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {

 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";

 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  
  System.setProperty("javax.net.debug", "ssl,handshake");

  SSLClient client = new SSLClient();
  Socket s = client.clientWithoutCert();

  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s
    .getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() throws Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }
}


可以看到,除了把一些类变成SSL通信类以外,客户端也多出了使用信任证书仓库的代码。以上,我们便完成了SSL单向握手通信。即:客户端验证服务端的证书,服务端不认证客户端的证书。

以上便是Java环境下SSL单向握手的全过程。因为我们在客户端设置了日志输出级别为DEBUG:


Java代码 
1.System.setProperty("javax.net.debug", "ssl,handshake"); 
System.setProperty("javax.net.debug", "ssl,handshake");

因此我们可以看到SSL通信的全过程,这些日志可以帮助我们更具体地了解通过SSL协议建立网络连接时的全过程。

结合日志,我们来看一下SSL双向认证的全过程:

 

第一步: 客户端发送ClientHello消息,发起SSL连接请求,告诉服务器自己支持的SSL选项(加密方式等)。


Bash代码 
1.*** ClientHello, TLSv1 
*** ClientHello, TLSv1


第二步: 服务器响应请求,回复ServerHello消息,和客户端确认SSL加密方式:


Bash代码 
1.*** ServerHello, TLSv1 
*** ServerHello, TLSv1


第三步: 服务端向客户端发布自己的公钥。

第四步: 客户端与服务端的协通沟通完毕,服务端发送ServerHelloDone消息:


Bash代码 
1.*** ServerHelloDone 
*** ServerHelloDone


第五步: 客户端使用服务端给予的公钥,创建会话用密钥(SSL证书认证完成后,为了提高性能,所有的信息交互就可能会使用对称加密算法),并通过ClientKeyExchange消息发给服务器:


Bash代码 
1.*** ClientKeyExchange, RSA PreMasterSecret, TLSv1 
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1


第六步: 客户端通知服务器改变加密算法,通过ChangeCipherSpec消息发给服务端:


Bash代码 
1.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
main, WRITE: TLSv1 Change Cipher Spec, length = 1


第七步: 客户端发送Finished消息,告知服务器请检查加密算法的变更请求:


Bash代码 
1.*** Finished 
*** Finished


第八步:服务端确认算法变更,返回ChangeCipherSpec消息


Bash代码 
1.main, READ: TLSv1 Change Cipher Spec, length = 1 
main, READ: TLSv1 Change Cipher Spec, length = 1


第九步:服务端发送Finished消息,加密算法生效:


Bash代码 
1.*** Finished 
*** Finished


那么如何让服务端也认证客户端的身份,即双向握手呢?其实很简单,在服务端代码中,把这一行:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(false); 
((SSLServerSocket) _socket).setNeedClientAuth(false);

改成:


Java代码 
1.((SSLServerSocket) _socket).setNeedClientAuth(true); 
((SSLServerSocket) _socket).setNeedClientAuth(true);

就可以了。但是,同样的道理,现在服务端并没有信任客户端的证书,因为客户端的证书也是自己生成的。所以,对于服务端,需要做同样的工作:把客户端的证书导出来,并导入到服务端的证书仓库:


Bash代码 
1.keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer  
2.Enter keystore password:  client  
3.Certificate stored in file <client_key.cer> 
keytool -export -alias bluedash-ssl-demo-client -keystore ./client_ks -file client_key.cer
Enter keystore password:  client
Certificate stored in file <client_key.cer>


Bash代码 
1.keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks  
2.Enter keystore password:  server  
3.Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
4.Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn  
5.Serial number: 4c57c80b  
6.Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010 
7.Certificate fingerprints:  
8.         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79 
9.         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2  
10.         Signature algorithm name: SHA1withRSA  
11.         Version: 3 
12.Trust this certificate? [no]:  yes  
13.Certificate was added to keystore 
keytool -import -trustcacerts -alias bluedash-ssl-demo-client -file ./client_key.cer -keystore ./server_ks
Enter keystore password:  server
Owner: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Issuer: CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn
Serial number: 4c57c80b
Valid from: Tue Aug 03 15:40:59 CST 2010 until: Mon Nov 01 15:40:59 CST 2010
Certificate fingerprints:
         MD5:  DB:91:F4:1E:65:D1:81:F2:1E:A6:A3:55:3F:E8:12:79
         SHA1: BF:77:56:61:04:DD:95:FC:E5:84:48:5C:BE:60:AF:02:96:A2:E1:E2
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

完成了证书的导入,还要在客户端需要加入一段代码,用于在连接时,客户端向服务端出示自己的证书:


Java代码 
1.package org.bluedash.tryssl;  
2. 
3.import java.io.BufferedReader;  
4.import java.io.FileInputStream;  
5.import java.io.InputStreamReader;  
6.import java.io.PrintWriter;  
7.import java.net.Socket;  
8.import java.security.KeyStore;  
9.import javax.net.SocketFactory;  
10.import javax.net.ssl.KeyManagerFactory;  
11.import javax.net.ssl.SSLContext;  
12.import javax.net.ssl.SSLSocketFactory;  
13. 
14.public class SSLClient {  
15.    private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";  
16.    private static String CLIENT_KEY_STORE_PASSWORD = "456456";  
17.      
18.    public static void main(String[] args) throws Exception {  
19.        // Set the key store to use for validating the server cert.  
20.        System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);  
21.        System.setProperty("javax.net.debug", "ssl,handshake");  
22.        SSLClient client = new SSLClient();  
23.        Socket s = client.clientWithCert();  
24.          
25.        PrintWriter writer = new PrintWriter(s.getOutputStream());  
26.        BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));  
27.        writer.println("hello");  
28.        writer.flush();  
29.        System.out.println(reader.readLine());  
30.        s.close();  
31.    }  
32. 
33.    private Socket clientWithoutCert() throws Exception {  
34.        SocketFactory sf = SSLSocketFactory.getDefault();  
35.        Socket s = sf.createSocket("localhost", 8443);  
36.        return s;  
37.    }  
38. 
39.    private Socket clientWithCert() throws Exception {  
40.        SSLContext context = SSLContext.getInstance("TLS");  
41.        KeyStore ks = KeyStore.getInstance("jceks");  
42.          
43.        ks.load(new FileInputStream(CLIENT_KEY_STORE), null);  
44.        KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");  
45.        kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());  
46.        context.init(kf.getKeyManagers(), null, null);  
47.          
48.        SocketFactory factory = context.getSocketFactory();  
49.        Socket s = factory.createSocket("localhost", 8443);  
50.        return s;  
51.    }  
52.} 
package org.bluedash.tryssl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

public class SSLClient {
 private static String CLIENT_KEY_STORE = "/Users/liweinan/projs/ssl/src/main/resources/META-INF/client_ks";
 private static String CLIENT_KEY_STORE_PASSWORD = "456456";
 
 public static void main(String[] args) throws Exception {
  // Set the key store to use for validating the server cert.
  System.setProperty("javax.net.ssl.trustStore", CLIENT_KEY_STORE);
  System.setProperty("javax.net.debug", "ssl,handshake");
  SSLClient client = new SSLClient();
  Socket s = client.clientWithCert();
  
  PrintWriter writer = new PrintWriter(s.getOutputStream());
  BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
  writer.println("hello");
  writer.flush();
  System.out.println(reader.readLine());
  s.close();
 }

 private Socket clientWithoutCert() tbhrows Exception {
  SocketFactory sf = SSLSocketFactory.getDefault();
  Socket s = sf.createSocket("localhost", 8443);
  return s;
 }

 private Socket clientWithCert() throws Exception {
  SSLContext context = SSLContext.getInstance("TLS");
  KeyStore ks = KeyStore.getInstance("jceks");
  
  ks.load(new FileInputStream(CLIENT_KEY_STORE), null);
  KeyManagerFactory kf = KeyManagerFactory.getInstance("SunX509");
  kf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
  context.init(kf.getKeyManagers(), null, null);
  
  SocketFactory factory = context.getSocketFactory();
  Socket s = factory.createSocket("localhost", 8443);
  return s;
 }
}

通过比对单向认证的日志输出,我们可以发现双向认证时,多出了服务端认证客户端证书的步骤:


Bash代码 
1.*** CertificateRequest  
2.Cert Types: RSA, DSS  
3.Cert Authorities:  
4.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
5.<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>  
6.*** ServerHelloDone 
*** CertificateRequest
Cert Types: RSA, DSS
Cert Authorities:
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
<CN=localhost, OU=cn, O=cn, L=cn, ST=cn, C=cn>
*** ServerHelloDone

 

Bash代码 
1.*** CertificateVerify  
2.main, WRITE: TLSv1 Handshake, length = 134 
3.main, WRITE: TLSv1 Change Cipher Spec, length = 1 
*** CertificateVerify
main, WRITE: TLSv1 Handshake, length = 134
main, WRITE: TLSv1 Change Cipher Spec, length = 1


在 @*** ServerHelloDone@ 之前,服务端向客户端发起了需要证书的请求 @*** CertificateRequest@ 。

在客户端向服务端发出 @Change Cipher Spec@ 请求之前,多了一步客户端证书认证的过程 @*** CertificateVerify@ 。

客户端与服务端互相认证证书的情景

posted @ 2012-07-13 13:50 chen11-1 阅读(3374) | 评论 (0)编辑 收藏

Web 应用测试接口即将规范化 W3C

WebDriver API的首个草案已经交付W3C了,该API提供了一个用于远程控制浏览器的接口,该功能主要用于Web应用程序的自动化测试。
WebDriver API与Web测试框架Selenium 2中的具有同样名称的接口非常相似,其设计的核心是WebElement,这是函数findElement()返回的一个DOM对象。它需要一个用于定位元素的定位器(Locator)。该API允许元素通过XPath、ID、CSS选择器或链接文本被调用。

此外,该API还具有其他方法,允许你读取并设置各种WebElements属性。比如决定使用哪个字符串填充文本框、点击哪个按钮触发tb事件、select元素的实际选择等。

一些类似于Selenium 1中的DOM事件触发功能现在可以通过JavaScript代码在W3C API和Selenium 2中实现。

 

posted @ 2012-07-13 13:48 chen11-1 阅读(775) | 评论 (0)编辑 收藏

Oracle安全:SCN可能最大值与耗尽问题

在2012年第一季度的CPU补丁中,包含了一个关于SCN修正的重要变更,这个补丁提示,在异常情况下,Oracle的SCN可能出现异常增长,使得数据库的一切事务停止,由于SCN不能后退,所以数据库必须重建,才能够重用。

我曾经在以下链接中描述过这个问题:

http://www.eygle.com/archives/2012/03/oracle_scn_bug_exhaused.html

Oracle使用6 Bytes记录SCN,也就是48位,其最大值是:


SQL> col scn for 999,999,999,999,999,999  SQL> select power(2,48) scn from dual;  SCN  ------------------------  281,474,976,710,656

Oracle在内部控制每秒增减的SCN不超过 16K,按照这样计算,这个数值可以使用大约544年:


SQL> select power(2,48) / 16 / 1024 / 3600 / 24 / 365 from dual;  POWER(2,48)/16/1024/3600/24/365  -------------------------------  544.770078

然而在出现异常时,尤其是当使用DB Link跨数据库查询时,SCN会被同步,在以下链接中,我曾经描述过此问题:

http://www.eygle.com/archives/2006/11/db_link_checkpoint_scn.html

一个数据库当前最大的可能SCN被称为"最大合理SCN",该值可以通过如下方式计算:

col scn for 999,999,999,999,999,999  select (  (   (  (  (  (  to_char(sysdate,'YYYY')-1988  )*12+  to_char(sysdate,'mm')-1  )*31+to_char(sysdate,'dd')-1  )*24+to_char(sysdate,'hh24')  )*60+to_char(sysdate,'mi')  )*60+to_char(sysdate,'ss')  ) * to_number('ffff','XXXXXXXX')/4 scn  from dual  / 这个算法即SCN算法,以1988年1月1日 00点00时00分开始,每秒计算1个点数,最大SCN为16K。

这个内容可以参考如下链接:

http://www.eygle.com/archives/2006/01/how_big_scn_can_be.html

在CPU补丁中,Oracle提供了一个脚本 scnhealthcheck.sql 用于检查数据库当前SCN的剩余情况。

该脚本的算法和以上描述相同,最终将最大合理SCN 减去当前数据库SCN,计算得出一个指标:HeadRoom。也就是SCN尚余的顶部空间,这个顶部空间最后折合成天数:

以下是这个脚本的内容:

Rem  Rem $Header: rdbms/admin/scnhealthcheck.sql st_server_tbhukya_bug-13498243/8 2012/01/17 03:37:18 tbhukya Exp $  Rem  Rem scnhealthcheck.sql  Rem  Rem Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.  Rem  Rem NAME Rem scnhealthcheck.sql - Scn Health check Rem  Rem DESCRIPTION  Rem Checks scn health of a DB  Rem  Rem NOTES  Rem .  Rem  Rem MODIFIED (MM/DD/YY)  Rem tbhukya 01/11/12 - Created  Rem  Rem  define LOWTHRESHOLD=10  define MIDTHRESHOLD=62  define VERBOSE=FALSE set veri off;  set feedback off;  set serverout on DECLARE verbose boolean:=&&VERBOSE;  BEGIN For C in (  select version,  date_time,   dbms_flashback.get_system_change_number current_scn,  indicator  from (  select version,  to_char(SYSDATE,'YYYY/MM/DD HH24:MI:SS') DATE_TIME,  ((((  ((to_number(to_char(sysdate,'YYYY'))-1988)*12*31*24*60*60) +  ((to_number(to_char(sysdate,'MM'))-1)*31*24*60*60) +  (((to_number(to_char(sysdate,'DD'))-1))*24*60*60) +  (to_number(to_char(sysdate,'HH24'))*60*60) +  (to_number(to_char(sysdate,'MI'))*60) +  (to_number(to_char(sysdate,'SS')))  ) * (16*1024)) - dbms_flashback.get_system_change_number)  / (16*1024*60*60*24)  ) indicator  from v$instance  )  ) LOOP  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  dbms_output.put_line( 'ScnHealthCheck' );  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  dbms_output.put_line( 'Current Date: '||C.date_time );  dbms_output.put_line( 'Current SCN: '||C.current_scn );  if (verbose) then dbms_output.put_line( 'SCN Headroom: '||round(C.indicator,2) );  end if;  dbms_output.put_line( 'Version: '||C.version );  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  IF C.version > '10.2.0.5.0' and C.version NOT LIKE '9.2%' THEN IF C.indicator>&MIDTHRESHOLD THEN dbms_output.put_line('Result: A - SCN Headroom is good');  dbms_output.put_line('Apply the latest recommended patches');  dbms_output.put_line('based on your maintenance schedule');  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=' || '24 after apply.');  END IF;  ELSIF C.indicator<=&LOWTHRESHOLD THEN dbms_output.put_line('Result: C - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now' );  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('set _external_scn_rejection_threshold_hours=24 ' || 'after apply');  END IF;  dbms_output.put_line('AND contact Oracle support immediately.' );   ELSE dbms_output.put_line('Result: B - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now');  IF (C.version < '11.2.0.2') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=' ||'24 after apply.');  END IF;  END IF;  ELSE IF C.indicator<=&MIDTHRESHOLD THEN dbms_output.put_line('Result: C - SCN Headroom is low');  dbms_output.put_line('If you have not already done so apply' );  dbms_output.put_line('the latest recommended patches right now' );  IF (C.version >= '10.1.0.5.0' and C.version <= '10.2.0.5.0' and C.version NOT LIKE '9.2%') THEN dbms_output.put_line(', set _external_scn_rejection_threshold_hours=24' || ' after apply');  END IF;  dbms_output.put_line('AND contact Oracle support immediately.' );  ELSE dbms_output.put_line('Result: A - SCN Headroom is good');  dbms_output.put_line('Apply the latest recommended patches');  dbms_output.put_line('based on your maintenance schedule ');  IF (C.version >= '10.1.0.5.0' and C.version <= '10.2.0.5.0' and C.version NOT LIKE '9.2%') THEN dbms_output.put_line('AND set _external_scn_rejection_threshold_hours=24' || ' after apply.');  END IF;  END IF;  END IF;  dbms_output.put_line(  'For further information review MOS document id 1393363.1');  dbms_output.put_line( '-----------------------------------------------------' || '---------' );  END LOOP;  end;  / 在应用补丁之后,一个新的隐含参数 _external_scn_rejection_threshold_hours 引入,通常设置该参数为 24 小时:

_external_scn_rejection_threshold_hours=24

这个设置降低了SCN Headroom的顶部空间,以前缺省的设置容量至少为31天,降低为 24 小时,tb可以增大SCN允许增长的合理空间。

但是如果不加控制,SCN仍然可能会超过最大的合理范围,导致数据库问题。

这个问题的影响会极其严重,我们建议用户检验当前数据库的SCN使用情况,以下是检查脚本的输出范例:

--------------------------------------  ScnHealthCheck  --------------------------------------  Current Date: 2012/01/15 14:17:49   Current SCN: 13194140054241  Version: 11.2.0.2.0  --------------------------------------  Result: C - SCN Headroom is low  If you have not already done so apply  the latest recommended patches right now  AND contact Oracle support immediately.  For further information review MOS document id 1393363.  -------------------------------------- 这个问题已经出现在客户环境中,需要引起大家的足够重视。

 

posted @ 2012-07-10 11:12 chen11-1 阅读(1014) | 评论 (1)编辑 收藏

详解Oracle数据库优化方案与实践

在这里我们将介绍Oracle数据库优化方案与实践,不同的环境会有不同的调试,但是也会有差别,希望大家能合理的吸收。

一、前言

二、ORACLE数据库优化概述

1、内存等参数配置的优化

2、减少物理读写的优化

3、批量重复操作的SQL语句及大表操作的优化

二、ORACLE数据库优化方案

1、内存等Oracle系统参数配置

2、使用索引

3、表分区

4、Procedure优化

5、其他改造

6、维护作业计划

三、ORACLE数据库优化前后比较

1、批量重复的SQL语句执行性能

2、一些单次、不常用的操作的语句执行性能

四、参考

1、常用的优化工具

2、参考文献

一、前言

随着实际项目的启动,实际项目中使用的 Oracle数据库经过一段时间的运行,在线保存的数据量和业务处理的数据量在逐渐增大,最初的Oracle设置,与现在实际需要的运行性能有一定差距,需要进行一些优化调整。

本文将结合本人实际维护经验,相应地提出实际项目数据处理的一些优化方法,以供参考。

适用于Oracle 9i。

二、Oracle数据库优化概述

Oracle数据库的优化,针对不同的应用,会有侧重点不同的优化方法,根据我们实际项目的应用特点,我们主要关心的是每次事务执行完成的时间长短。

从Oracle数据库本身的特点,我们可以把优化工作划分为初始优化设置,微优化。

在初始优化设置时,我们只能根据硬件情况,估计业务运行的情况,综合经验,给出一种经验设置,大体上来说,这种经验设置离满足优化需求的目标不是很远。在完成了初始优化设置后,经过一段时间的业务运行,已可开始收集实际运行环境的性能数据,此时,就可以对各种Oracle性能指标、各种关心的事务操作进行性能评估,然后进行微优化了。

Oracle优化,不是一个一蹴而就的工作,也不是一个一劳永逸的工作,需要定期维护,tb定期观察,在发现性能瓶颈时及时进行调整。Oracle总是存在性能瓶颈的,不使用、不操作的数据库总是最快的,在解决当前瓶颈后,总是会有另一个瓶颈出现,所以在优化前,我们需要确定一个优化目标,我们的目标是满足我们的应用性能要求就可以了。

Oracle优化,涉及的范围太广泛,包含的有主机性能,内存使用性能,网络传输性能,SQL语句执行性能等等,从我们面向网管来说,满足事务执行速度性能主要表现在:

1)批量重复的SQL语句执行性能(主要是通过Procedure计算完成数据合并和数据汇总的性能和批量数据采集入库的性能);

2)一些单次、不常用的操作的语句执行性能(主要是GUI的非规律操作)。

根据这两个特点,我们可把优化方法归纳到3个重要方向:

1)内存等参数配置的优化。内存优化,是性能受益最快的地方。

2)减少物理读写的优化。内存逻辑I/O操作的时间,远远小于物理I/O的操作时间。

3)批量重复操作的SQL语句及大表操作的优化。减少SQL执行次数,减少大表操作次数。

下面主要针对得益最大的这三个方向的优化进行阐述。

1、内存等参数配置的优化

对于大多数应用来说,最直接、最快速得到优化收益的,肯定属于内存的优化。给每个Oracle内存块分配合理的大小,可以有效的使用数据库。通过观察各种数据库活动在内存里的命中率,执行情况,我们能很快的掌握数据库的主要瓶颈。我们从下面的一条SQL语句的执行步骤就可知道。

一个SQL语句,从发布到执行,会按顺序经历如下几个步骤:

1)Oracle把该SQL的字符转换成它们的ASCII等效数字码。

2)该ASCII数字码被传送给一个散列算法,生成一个散列值。

3)用户server process查看该散列值是否在shared pool内存块中存在。

若存在:

4)使用shared pool中缓存的版本来执行。

若不存在:

4)检查该语句的语义正确性。

5)执行对象解析(这期间对照数据字典,检查被引用的对象的名称和结构的正确性)。

6)检查数据字典,收集该操作所引用的所有对象的相关统计数据。

7)准备执行计划,从可用的执行计划中选择一个执行计划。(包括对stored outline和materialized view的相关使用的决定)

8)检查数据字典,确定所引用对象的安全性。

9)生成一个编译代码(P-CODE)。

10)执行。

这里,通过内存的合理分配,参数的合理设置,我们主要解决:

1)减少执行到第五步的可能,节约SQL语句解析的时间。第五步以后的执行过程,是一个很消耗资源的操作过程。

2)通过内存配置,尽可能让SQL语句所做的操作和操作的数据都在内存里完成。大家都知道,从内存读取数据的速度,要远远快于从物理硬盘上读数据,一次内存排序要比硬盘排序快很多倍。

3)根据数据库内存活动,减少每个内存块活动的响应时间,充分利用每个内存块,减少内存latch争用发生的次数。

2、减少物理读写的优化

无论如何配置Oracle数据库,我们的网管系统,每小时周期性的都会有新数据被处理,就会发生物理读写,这是避免不了的。

减少物理读写的优化,一般所用的方法有:

1) 增加内存data buffer的大小,尽可能让数据库操作的数据都能在内存里找到,不需要进行物理读写操作。

2) 通过使用索引,避免不必要的全表扫描。

3) 大表物理分区,Oracle具有很好的分区识别功能,减少数据扫描范围。

上述3个方法,是从整体上改善数据库物理I/O性能最明显的3个方法。能非常快速的减少数据库在物理I/O,最直接的反应是数据库事务执行时间能能以数量级为单位减少。其他的一些减少物理读写的优化方法,比如使用materialized view,Cluster等方法;还有一些分散I/O的方法,比如 Oracle日志文件不与数据文件放在一个物理硬盘,数据热点文件物理I/O分开等等方法,就目前我们的网管系统而言,能得到的效果不是很明显,在网管系统中,为了不增加数据库维护的复杂性,不推荐使用。

3、批量重复操作的SQL语句及大表操作的优化

批量重复执行的SQL语句,一般出现在每个周期时间内的数据批量入库的insert语句,和数据合并、汇总的周期性select、delete、insert操作。

我们需要注意以下几点:

1) 减少不必要的SQL语句执行和SQL语句的执行次数。

每条SQL语句执行,都会消费系统资源,都有执行时间。减少不必要的SQL语句执行和减少SQL语句的执行次数,自然能减少业务执行时间。需要根据业务流程,重新设计数据处理的代码。此方法主要适用于procedure执行的数据合并、汇总。

2) 这些SQL语句,由于每个SQL语句都要执行很多次,应该尽量让该SQL的散列值在shared pool内存块中存在。也就是使用动态SQL,避免SQL硬解析。

可通过Oracle参数的设置,和动态SQL语句的应用,通过绑定变量的方式,减少SQL语句的解析次数。

3)减少大表的操作,确保在一次事务中,同类操作只对大表执行一次。主要在数据合并和数据汇总的pprocedure和数据采集时出现

三、Oracle数据库优化方案

1、内存等Oracle系统参数配置

Oracle 的parameter参数,分动态参数和静态参数,静态参数需要重新启动数据库才能生效,动态参数不需要重新启动数据库即可生效。

Oracle 9i可以使用spfile的特性,使用alter system set 参数名=参数值 scope=both[spfile];的方法进行修改。也可以直接修改pfile。

以下给出了网管Oracle 数据库重点关注的parameter的初始优化设置。

最大可使用的内存SGA总和

静态参数sga_max_size=物理内存的大小减1.5G

Shared pool

动态参数shared_pool_size= 600 ~ 800 M

静态参数shared_pool_reserved_size= 300 M

动态参数open_cursors= 400 ~ 600

静态参数cursor_space_for_time= TRUE

静态参数session_cached_cursors= 60 ~ 100

动态参数cursor_sharing= SIMILAR

Data buffer

动态参数db_cache_advice= READY

动态参数db_cache_size

动态参数Db_keep_cache_size

动态参数db_recycle_cache_size

(sga_max_size大小,除了分配给所有非data buffer的size,都分配给data buffer)

Sga other memory

动态参数large_pool_size= 50 M

静态参数java_pool_size= 100 M

动态参数log_buffer= 3 M

Other memory

动态参数sort_area_size= 3 M

静态参数sort_area_retained_size= 0.5 M

静态参数pga_aggregate_target= 800 M

动态参数workarea_size_policy= AUTO

磁盘I/O配置

静态参数sql_trace= FALSE

动态参数timed_statistics= true

动态参数db_file_multiblock_read_count= 16

静态参数dbwr_io_slaves= 0

静态参数db_writer_processes= 3

静态参数undo_management= AUTO

动态参数undo_retention= 7200

2、使用索引

我们初步定义,表数据超过1000行的表,我们都要求使用索引。(不区分事务操作的数据在表数据中所占的比例)

索引所包含的字段不超过4个。

检查SQL语句是否使用了索引,我们使用execute plan来看,获得explain的方法,我们通过SQL*PLUS工具,使用如下命令进行查看:

setautotraceonsetautotracetraceonlyexplain settimingon或通过SQL*PLUS trace,然后查看user_dump_dest下的跟踪文件,使用tkprof工具格式化后阅览。

altersessionsetevents'10046tracenamecontextforever,level12'; altersessionsetevents'10046tracenamecontextoff'; SELECTp.spid,s.usernameFROMv$sessions,v$processpWHEREs.audsid=USERENV('sessionid')ANDs.paddr=p.addr;3、表分区

在网管数据库里,比较突出的大表有小区表和告警表。

性能表,使用范围分区。

以时间点start_time为范围分区字段。

告警表,使用range-hash的混合分区和范围分区。

范围分区以时间点starttime为分区字段,混合分区增加ALARMNUMBER为字段的hash子分区。

同时,创建本地分区索引。

4、Procedure优化

1)取消地市一级的Procedure,只保留其上层调用Procedure,并保持参数输入方法,调用方法不变。

2)确保大表数据查询操作只有1次,确保大表数据删除只有一次。

3)确保单条SQL语句执行已优化。

4)减少SQL执行次数。

5、其他改造

修改表存储参数,提前预先分配extents。

修改表空间存储参数(采集表空间所用块设置为大块,比如32k一个块;修改ptcfree,pctused,pctincrease等)。

避免使用唯一索引和非空约束。

创建合理的索引。

各模块SQL语句优化,比如使用提示固定索引等。

确认每一条历史数据删除语句已优化和删除方法。

临时表的使用。

6、维护作业计划

表分析(包含确定具体的表的分析方法,分区表分析方法,索引分析方法)。

空间回收维护(包括确定HWM,回收多余分配给表的块,合并数据块碎片等)。

索引维护(包括定期重建索引,索引使用情况监视等)。

历史数据删除检查(检查保存的数据是否符合要求,检查历史数据删除方法是否正确-比如批量删除提交的方法等)。

全库性能分析和问题报告及优化(比如使用statspack进行性能趋势分析,检查有问题的SQL或事务,确定当前系统等待的top 5事件等等)。

表数据keep,default及reclye(比如把一些常用的配置表固定在内存里等)。

数据库参数核查(防止数据库参数被修改,定期对系统配置参数进行比较)。

日志文件分析(定期检查Oracle生成的日志文件,定期备份、删除)。

硬盘空间维护(定期对Oracle 对象使用的空间情况进行监视)。

四,Oracle数据库优化前后比较

1、批量重复的SQL语句执行性能

根据网元数量,各地的执行的完成时间有所区别。

用于数据合并和汇总的Procedure的计算性能

通过statspack的周期性采集数据,我们可以使用以下语句,计算我们想统计的Procedure的执行情况:

SELECTTO_CHAR(sn.snap_time,'yyyy-mm-ddhh24:mi:ss')ASsnap_time,s.disk_reads, s.buffer_gets,s.elapsed_time/1000000ASelapsedtime FROM(SELECThash_value,sql_text,address,last_snap_id FROMSTATS$SQLTEXTWHEREpiece=0ANDsql_textLIKE'%&sqltext_key%')t, (SELECTaddress,hash_value,snap_id,sql_text,disk_reads,executions, buffer_gets,rows_processed,elapsed_time FROMSTATS$SQL_SUMMARY)s,STATS$SNAPSHOTsn WHEREs.hash_value=t.hash_value ANDs.address=t.address ANDs.snap_id=t.last_snap_id ANDsn.snap_id=s.snap_id;比如,我们以perfstat用户执行该SQL,输入“to_comp”,可以观察到数据库里保存的有的to_comp存储过程的执行时间,我们发现,其执行时间,从优化前的几千秒,最后稳定在优化后的几十秒。

注:to_comp是整体调用执行一次所有网元的数据合并和汇总的procedure。

用于小区分析数据的Procedure的计算性能

使用上面的方法,我们一样可以知道,小区分析的procedure执行,从优化前的约几千秒,最后稳定在优化后的几十秒。

批量数据采集入库性能

使用bcp,能从以前约15分钟,减少到约4分钟。

2、一些单次、不常用的操作的语句执行性能

GUI上的性能数据查询,告警数据查询,响应时间都极快,几乎不再出现长时间等待响应的情况。

五,参考

常用的优化工具

statspack

sql*plus

TOAD

 

posted @ 2012-07-10 11:10 chen11-1 阅读(1782) | 评论 (1)编辑 收藏

Oracle容灾方案的选择

容灾方案的选择
---- 容灾首先是一个概念,要认识到为什么做容灾,才能做好容灾。世界上没有卖后悔药的,当灾难降临了,如果没有行之有效的数据保护、数据恢复的容灾措施,带来不可预估的损失将是无法避免的。类似电信行业、金融行业,证券行业也是如此,动辄涉及数以百亿计的资金、涉及庞大的客户量,在系统数据的准确、业务的连续、关键业务的不中断等方面更是不容出现任何的差错。
 
----目前,业界具有容灾功能的常用解决方案主要包括以下几类:磁盘阵列复制技术,主要由一些磁盘阵列厂商提供,如EMC SRDF、IBM PPRC 、HP BusinessCopy、HDS TrueCopy、tb等;存储卷复制技术,由一些卷管理软件厂商提供,如VERITAS VVR;数据库复制技术,由数据库厂商以及一些第三方厂商提供,如DSG RealSync,Quest SharePlex等;应用层复制技术,由各系统的应用厂商自己提供。
 
----磁盘阵列复制技术主要适用于数据中心级的海量数据复制,此技术用户必需采用支持该功能的磁盘阵列型号,而这些阵列大都为高端阵列,投资非常昂贵。并且,由于政府行业用户的带宽有限,而磁盘阵列复制技术对带宽的要求又相对很高,动辄需要上GB的带宽。此外,采用磁盘阵列复制技术,其目标端无法提供实时数据查询,由于目标端数据库在复制过程中不能被打开,难于实现交易与查询的分离,同时也造成大量投资浪费。因此,磁盘阵列复制技术无法满足某些行业集中交易系统的容灾需求,使得这些用户难以选择此种解决方案。
 
----存储卷复制技术主要适用于工作组级的数据复制,它对CPU资源占用高。同样由于目标端数据无法提供实时数据查询和对带宽的要求高,使得证券等行业用户也难以选择。
 
----而应用层复制技术只适合那些在应用中提供了该技术的应用,由于它的非标准化、开发和维护工作量大,使得其应用不成熟也不普遍。关键行业对数据的可靠性要求又非常之高,使得关键行业用户也不敢冒然选择此种复制技术。
 
----DSG RealSync属于数据库复制技术,它适用于从工作组级、企业级到数据中心级的复制需求,无论系统采用什么样的服务器平台、什么样的存储平台,只要是ORACLE系统之间的复制即可适用。采用DSG RealSync复制技术,其目标端数据库在复制过程中处于可用状态,帮助关键行业用户实现生产系统与查询统计报表系统的分离;其源端系统和目标端系统可以采用异构的操作系统平台、存储平台;支持选择性复制,即支持只复制指定的user、指定的Table、指定的行和列,从而节省存储空间,提高应用灵活性;支持1对多,多对1的复制结构,即:能够将多个数据库中的数据复制到一个数据库中,tb能够将一个数据库中的不同数据分发到不同的数据库中;也节约带宽和网络资源,其所需带宽一般在几Mbps,几十Mbps。
 
----随着用户容灾意识的逐渐增强,关键行业也提出了建设一套高效、可靠、投资回收比高的灾难备份系统的需求,以确保系统的数据安全和灾难发生时数据的快速恢复。  
 

posted @ 2012-07-10 11:08 chen11-1 阅读(1184) | 评论 (0)编辑 收藏

Oracle Buys Finnish Open-Source Developer

Oracle scooped up another small technology company on Friday, announcing its acquisition of Finnish open-source database technology developer Innobase. Financial terms of the deal were not disclosed.

Innobase, based in Helsinki, is the creator of InnoDB, an add-on storage engine for MySQL. InnoDB is distributed under the GNU GPL (General Public License) open-source license. The software is bundled with MySQL through a contractual agreementb that comes up for renewal next year; Oracle said it expects to negotiate an extension of that contract.


"Oracle intends to continue developing the InnoDB technology and expand our commitment to open source software," Charles Rozwat, Oracle's executive vice president in charge of database and middleware technology, said in a prepared statement. "Oracle has already developed and contributed an open source clustered file system to Linux. We expect to make additional contributions in the future," he added.


Oracle, headquartered in Redwood Shores, California, has arranged around a dozen acquisitions in the past year, most of them significantly smaller than its blockbuster, multibillion-dollar PeopleSoft and Siebel Systems buys. Other recent purchases include supply-chain logistics software maker Global Logistics Technologies, retail optimization software developer ProfitLogic, database performance enhancement technology company TimesTen, and a majority stake in Indian banking software vendor i-flex Solutions.

 

posted @ 2012-07-10 11:05 chen11-1 阅读(1161) | 评论 (0)编辑 收藏

IT人士遭遇早衰危机 对象难找老婆难陪

 IT 行业,说起来就让很多人心动。为啥?专出富豪呗。2012年胡润少壮派富豪榜中,IT 行业可是最“丰产”的行业。

  做 IT 的人却叫苦连连:“这个叫隔行如隔山……事实上我们并没有外界想的那样光鲜,你们上班的时候我们上班,你们下班的时候我们加班……年轻的时候不觉得,到了 30 岁,忽然有些力不从心的感觉了。”

  这似乎是很多“80后”IT 人士的心声,在工作了四五年之后,他们一方面面临身体、精力的瓶颈,另一方面,“90后”又在后面虎视眈眈。

 

  31岁的 IT 精英找不到女友

  刘先生今年 31 岁,是典型的 IT 男,大学一毕业就进入某网络公司的研发部,到现在已经是公司元老中的元老,也是研发部举足轻重的人物。

  按理说,像刘先生这样的真没什么好担忧的,位高权重,收入颇丰,但他依然觉得苦恼——做 IT 太费脑,加班时间太多。“我们公司加班非常多,忙到晚上三四点是常有的事情,我们每个人的桌下甚至都放了个躺椅,太累的时候就在办公室里睡会儿。”

  刘先生告诉记者,前几年,他倒是每天都充满干劲,天天加班都无所谓,但现在明显觉得吃不消了,连着加班几天,会有疲惫感,体力明显不如从前。

  “说 IT 行业是吃青春饭的,这话一点不假。我最近在电视剧《心术》里看到一句话,把人比喻为红苹果、青苹果、烂香蕉,说刚进医院的护士是红苹果,上了一年夜班的护士是青苹果,脸色发青,上了好几年夜班的护士是烂香蕉。我觉得自己就是根烂香蕉,脸色很差,整个人看起来就是tb亚健康状态的样子,甚至看起来比同龄人老。我现在已经有很多白头发了,可以预见,如果照这样的强度工作下去,到 45 岁,我出门,估计孩子们都得叫我爷爷——满头白发了。”

  刘先生的表弟今年考大学,IT 精英表哥一直是他崇拜的对象,填志愿时更是对表哥读过的学校的计算机系蠢蠢欲动,并立志要成为表哥那样的 IT 精英。刘先生赶忙劝说:“做 IT 很辛苦的,常常加班,还很费脑,你还是再考虑考虑,别的行业也可以。”

  因为老加班,刘先生连女朋友都没有。31岁了,有些同学的孩子都会打酱油了,他还是光棍一个,这也给他很大的压力。“以前没感觉,单身也挺快乐的,工作之余自己一个人瞎乐。过年的时候回去参加同学会,看到很多同学都带孩子来了,我还形单影只,忽然觉得有些迷茫。这几年的时间都贡献给工作了,连交女朋友的时间都没有。”

  刘先生告诉记者,在他们公司,他基本上也是做到头了:升职基本上已经遭遇天花板;再搞研发身体也吃不消了;转管理岗位,公司并不认同,也没有办法给他合适的位置。现在他打算自己出来创业,正好有几个做 IT 的朋友也都有创业打算,他们打算成立软件公司,专门开发手机软件,未来几年应该还是比较有前途的。

  年薪 60 万的 IT 男没时间陪家人

  有同样困扰的还有陈西(化名),他今年 30 岁,24岁大学毕业就从事 IT 业,跳了两次槽,目前年薪 60 万元,在杭州某家电子商务公司里做研发。

  陈西说,他其实很享受工作带来的成就感,但对于为工作而付出的无数个夜晚的加班感觉很无奈。“这几年关于过劳死的报道很多,很多正值壮年的人因为工作强度太大而猝死。我的一个同事,因为这几年工作太拼命,最近被查出来肝方面的毛病。我忽然觉得有些恐惧,怕这样的事情发生在我身上。我今年刚刚当了父亲,有了对龙凤胎,我想好好珍惜生命,看他们长大。我现在特别想过朝九晚五的生活,下班后就回家陪女儿,而不是对着冰冷的电脑加班。我也觉得很对不起我的妻子,她怀孕的时候产检都是自己去的,我太忙了。我现在想留出更多的时间给家人。”

  陈西告诉记者,他觉得自己在工作上也遇到了瓶颈,公司竞争很激烈,进来的“90后”都很有冲劲,像他们这种“前浪”,一不留神就会被拍死在沙滩上。“像我们做 IT 的,不可能一直做技术,这也是个吃青春饭的行当,一定要在三十左右技术达到一定程度的时候就转,转到非技术方面,管理或者是其他方面,或者创业。总之不能一直做,会有一个瓶颈,到达一定程度后就不会再往上涨了。我的同事、同学们都有这种感觉,到了 30 岁,就需要改变。”

  采访中,记者了解到,很多 IT 精英都有像刘先生和陈西这样的迷茫,长时间超负荷的工作,让他们没有自己的时间,很多 IT 男到 30 岁还打着光棍,虽然收入不菲,但因没有时间,并不受女生的欢迎。

  有些悲观的 IT 男甚至还萌生了 40 岁退休的想法,希望从此远

posted @ 2012-07-06 15:46 chen11-1 阅读(1289) | 评论 (1)编辑 收藏

二叉树三种非递归遍历的区别

1 #include <iostream>
  2
  3 #define MAXN  100
  4 using namespace stbd;
  5
  6
  7 struct BTNode
  8 {
  9     char tag;
10     BTNode *left;
11     BTNode *right;
12 };
13
14 class BTree
15 {
16 private:
17     BTNode **root;
18     void BuildBTree(BTNode **root);
19
20 public:
21     /*递归版本*/
22     void PreVisit(BTNode *root);
23     void InVisit(BTNode *root);
24     void PostVisit(BTNode *root);
25
26     /*非递归版本*/
27     void NR_PreVisit(BTNode *root);
28     void NR_InVisit(BTNode *root);
29     void NR_PostVisit(BTNode *root);
30
31     BTree(BTNode **r);
32     BTree();
33 };
34
35 BTree::BTree()
36 {
37
38 }
39
40 BTree::BTree(BTNode **r)
41 {
42     root = r;
43     /*
44 *root = new BTNode; 45     (*root)->left = NULL;
46 (*root)->right = NULL; 47     */
48     BuildBTree(root);
49 }
50
51 /*先序方式插入结点*/
52 void BTree::BuildBTree(BTNode **root)
53 {
54     char c;
55    
56     c = getchar();
57     if(c == '#')
58         *root=NULL;
59     else{
60         *root = new BTNode;
61         (*root)->tag = c;
62         BuildBTree(&(*root)->left);
63         BuildBTree(&(*root)->right);
64     }
65 }
66
67 void BTree::PreVisit(BTNode *root)
68 {
69     if(root!=NULL)
70     {
71         printf("%c ", root->tag );
72         PreVisit(root->left);
73         PreVisit(root->right);
74     }
75 }
76
77 void BTree::InVisit(BTNode *root)
78 {
79     if(root!=NULL)
80     {
81         InVisit(root->left);
82         printf("%c ", root->tag );
83         InVisit(root->right);
84     }
85 }
86
87 void BTree::PostVisit(BTNode *root)
88 {
89     if(root!=NULL)
90     {
91         PostVisit(root->left);
92         PostVisit(root->right);
93         printf("%c ", root->tag );
94     }
95 }
96
97 void BTree::NR_PreVisit(BTNode *root)
98 {
99     BTNode *s[MAXN];
100     int top=0;
101
102     while(top!=0 || root!=NULL)
103     {
104         while(root!=NULL)
105         {
106             s[top] = root;
107             printf("%c ", s[top++]->tag);
108             root = root->left;
109         }
110         if(top>0)
111         {
112             root = s[--top];
113             root = root->right;
114         }
115     }
116 }
117
118 void BTree::NR_InVisit(BTNode *root)
119 {
120     BTNode *s[MAXN];
121     int top=0;
122    
123     while(top!=0 || root!=NULL)
124     {
125         while(root!=NULL)
126         {
127             s[top++]=root;
128             root = root->left;
129         }
130         if(top>0)
131         {
132             root = s[--top];
133             printf("%c ", root->tag);
134             root = root->right;
135         }
136     }
137 }
138
139 void BTree::NR_PostVisit(BTNode *root)
140 {
141     BTNode *s[MAXN], *tmp=NULL;
142     int top=0;
143
144     while(top!=0 || root!=NULL)
145     {
146         while(root!=NULL)
147         {
148             s[top++]=root;
149             root=root->left;
150         }
151         if(top>0)
152         {
153             root = s[--top];
154
155             /*右孩子不存在或者已经访问过,root出栈并访问*/
156             if( (root->right == NULL) || (root->right == tmp) ) 
157             {
158                 printf("%c ", root->tag);
159                 tmp = root;        //保存root指针
160                 root=NULL;         //当前指针置空,防止再次入栈
161             }
162
163             /*不出栈,继续访问右孩子*/
164             else
165             {
166                 top++;             //与root=s[--top]保持平衡
167                 root = root->right;
168             }
169         }
170     }
171 }
172
173 int main()
174 {
175     BTNode *root=NULL;
176     BTree bt(&root);  //头指针的地址
177    
178     bt.NR_PreVisit(root);
179     printf("\n");
180     bt.NR_InVisit(root);
181     printf("\n");
182     bt.NR_PostVisit(root);
183     printf("\n");
184     return 0;
185 }
复制代码

先上代码,tb带NR(Non-recursive)的表示非递归遍历。

 

测试数据:

124#8##5##369###7##

 

表示的二叉树:

用windows自带的画图画的,的确是粗糙了点。。。

 

测试结果:

1 2 4 8 5 3 6 9 7
4 8 2 5 1 9 6 3 7
8 4 5 2 9 6 7 3 1

 

 

一、关于二叉树的建立

 

  首先要注意二叉树的创建过程,这里用的是先序方式递归插入结点,所以输入数据的时候,必须按照先序方式输入,

左结点或右结点为空的,用#表示。否则,输入不会有响应,因为递归过程还未结束,按CTRL+Z也没用。当然可以用其

他方式插入(如中序递归插入,后序递归插入等)。

 

二、三种非递归遍历的区别

 

  前序、中序和后序的递归遍历方式比较简单,这里就不讲了。而非递归的遍历方式,只需要用数组存储结点指针,模拟系统栈的工作机制就可以了。

先说先序非递归遍历,按照根-左-右的方式访问的话,需要将当前结点压栈(同时打印当前结点信息),直到左子树为空(内层while);然后出栈,访问

右结点;后面的操作就跟前面的一样了(外层while)。

  对于中序非递归遍历,可以看到代码结构几乎一模一样,只是打印结点信息的位置不同而已。这是因为中序遍历是左-根-右,这样前序和中序非

递归遍历(根-左和左-根都是压栈动作,且出栈动作的对象都是父节点)是一致的。

 

  对于后序非递归遍历,因为后序遍历是左-右-根,根的访问是在右孩子之后,而这意味着两种情况:

  1、右孩子不为空(继续访问右孩子);

  2、右孩子为空,从左孩子返回,则需要访问根结点。

  为了区分这两种情况(物理形式上从左孩子返回,还是从右孩子返回来访问根节点),对于右孩子的访问又需要判断根结点的右孩子是否为空或者已

访问过(右子树已遍历过)。除这两种情况外,都不应该访问根节点,而是要继续进入右子树。

  

三、补充说明

 

  在后序非递归遍历的else语句中top++纯粹是为了使栈保持平衡,因为对于2)继续访问右孩子这种情况,不需要出栈,而前面的root[--top]包含

出栈操作,以此保证栈的正确性(当然可以有其他的处理,这里也是考虑到三种非递归遍历方式的统一性)。

  两个while不会提高程序的时间复杂度,因为二叉树的结点个数是固定的,内层while是为了提高算法的逻辑性。

 

四、递归->非递归

 

  另外,今天实习看到一个老师写的非递归代码,非常棒,赞一个!他仅仅是将程序的返回地址和函数的形参、局部变量都保存起来,然后在退出时

还原现场;同样是非递归,但是这种方式更接近编译器的处理方式,同操作系统的任务切换也比较一致;所以这种处理方法为递归自动转换为非递归奠

定了基础。

  分享一下他当场编写的非递归的汉诺塔:

 

复制代码
  1 #include <stdio.h>
  2 #include <iostream>
  3 
  4 using namespace std ;
  5 
  6 #define  MAXSIZE  1000 
  7 
  8 struct SNode
  9 {
 10     int  n;
 11     char from ;
 12     char to;
 13     char aux ;
 14     int  label ;
 15 } ;
 16 
 17 struct STK
 18 {
 19     
 20     SNode  stack[MAXSIZE] ;
 21     int sp  ;
 22     STK()
 23     {
 24         sp = 0 ;
 25     };
 26     void push (int n,char from,char to,char aux, int label )
 27     {
 28         if ( sp>= MAXSIZE )
 29         {
 30             printf ( "STK is full!\n" ) ;
 31         }
 32         stack[sp].n = n ;
 33         stack[sp].from = from ;
 34         stack[sp].to = to ;
 35         stack[sp].aux = aux ;
 36         stack[sp++].label = label ;
 37     };
 38     SNode POP()
 39     {
 40         if ( sp <=0 )
 41         {
 42             printf ( "STK is empty!\n" ) ;
 43         }
 44         return stack[--sp] ;
 45     };
 46 } ;
 47 
 48 void move(int n,char from,char to,char aux)
 49 {
 50     if(n==1)
 51     {
 52         cout<<"将#1盘从"<<from<<"移到"<<to<<endl;
 53 }
 54     else
 55     {
 56          move(n-1,from,aux,to);
 57          cout<<"将#"<<n<<"盘从"<<from<<"移到"<<to<<endl;
 58          move(n-1,aux,to,from);
 59 }
 60 }
 61 
 62 
 63 void move_stk(int n,char from,char to,char aux)
 64 {
 65     STK stk ;
 66     char tmp;
 67 S1:
 68     if(n==1)
 69     {
 70         cout<<"将#1盘从"<<from<<"移到"<<to<<endl;
 71     }
 72     else
 73    {
 74     stk.push (n,from,to,aux,2 ) ;
 75     n = n-1 ;
 76     tmp = to ;
 77     to = aux ;  
 78     aux = tmp ;
 79     goto S1;
 80          // move(n-1,from,aux,to);
 81 S2:
 82          cout<<"将#"<<n<<"盘从"<<from<<"移到"<<to<<endl;
 83 
 84     stk.push (n,from,to,aux,3 ) ;
 85     n = n-1 ;
 86     tmp = from ;
 87     from = aux ;  
 88     aux = tmp ;
 89     goto S1;
 90          // move(n-1,aux,to,from);
 91 }
 92 S3:
 93     if ( stk.sp > 0 )
 94     {
 95         SNode sn = stk.POP() ;
 96         n = sn.n ;
 97         from = sn.from;
 98         to = sn.to ;
 99         aux = sn.aux ;
100         if  ( 1 == sn.label  )
101             goto S1;
102         else if ( 2 == sn.label )
103             goto S2;
104         else 
105             goto S3;        
106     }
107 }
108 
109 
110 
111 int main(int argc, char * argv[])
112 {
113     move ( 3,'A','B', 'C' );
114     printf ( "================================\n" ) ;
115     move_stk ( 3,'A','B', 'C' );
116 
117     return 0;
118 }

posted @ 2012-07-06 15:45 chen11-1 阅读(2092) | 评论 (0)编辑 收藏

客户端通过 HTTP 代理与 Telnet 服务器通信

最近遇到一个业务需求:客户端自动登录远程Telnet服务器,然后自动发出一系列指令,返回指令执行结果。

这里,我采用 TcpClient 来与远程服务器Telnet服务通信(默认端口:23)。这方面,网络上有不少的代码与文章。

完成之后,因需求变更,遇到了一个新的问题,就是客户机器需要通过 HTTP 代理来连接公网的 Telnetb 服务器。在这种情况,TcpClien 连接需要经过 HTTP 代理来与服务器通信。这方面代码几番求助度娘、谷歌无果。没办法,自己分析 HTTP 代理。

 

代理过程:

(1)客户机连接代理服务器;

(2)代理服务器请求连接Telnet服务器,Telnet服务器返回响应;

(3)代理服务器将Telnet返回响应传给客户端。

 

HTTP/1.0协议支持的请求方法有:GET、POST、PUT、DELETE、CONNECT等。同样HTTP代理服务器也这些请求方法。如:使用 GET/POST 方法代理访问网页等。

现在,要用的是 CONNECT 请求方法,去连接 Telnet 服务器。如下:

CONNECT xxx.xxx.xxx.xxx:23 HTTP/1.0

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US)

xxx.xxx.xxx.xxx:23,为Telnet服务IP与端口。

 

接下来,就是利用HTTP代理,创建一个连接到Telnet服务器的 TcpCient 实例对象。

 

 


public TcpClient CreateTcpClient(string proxyHost, int proxyPort, string telnetHost, int telnetPort)
        {
            IPHostEntry entry = Dns.GetHostEntry(proxyHost);
            IPEndPoint ipEndPoint = new IPEndPoint(entry.AddressList[0], proxyPort);

            TcpClient tcpClient = new TcpClient(AddressFamily.InterNetwork);
            tcpClient.Connect(ipEndPoint); // 连接代理服务器.

            // CONNECT Telnet 服务器
            string connectCommand = string.Format("CONNECT {0}:{1} HTTP/1.0\nUser-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US)\n\n", telnetHost, telnetPort);
            byte[] buffer = Encoding.ASCII.GetBytes(connectCommand);
            tcpClient.GetStream().Write(buffer, 0, buffer.Length);

            buffer = new byte[512];
            int received = tcpClient.GetStream().Read(buffer, 0, 512);
            string receivedText = Encoding.ASCII.GetString(buffer, 0, received);
            // 成功.
            if (receivedText.IndexOf("200") > -1)
                return tcpClient;
            return null;
        }
 

 这样的 TcpClient 通过代理与 Telnet 服务通信成功。

 

posted @ 2012-07-06 15:42 chen11-1 阅读(1258) | 评论 (1)编辑 收藏

用html+js+css做一个模拟键盘

 这个键盘用html+js+css搞出来的,做这个没什么目的,纯粹觉得好玩。

    现在暂时的功能有:

    1、可按键跟踪

    2、可大小写切换

    3、可鼠标点击输入

    4、可移动键盘位置

    可拓展功能有:

    1、可改变键盘大小

    2、可改变主题

    3、对某些按键添加事件

    4、结合html5的canvas弄个打字游戏啥的(想想就有趣^_^)

    ps(本人js和css都是菜鸟一枚,不喜可以喷,但请勿涉及家人)

 

html

 

keytBoard.js

View Code
复制代码
  1 //移动键盘
  2 function dragMing(idORclass1, idORclass2) {
  3     var obj = this; //这里的this是指dragMing对象么
  4     this.idORclass1 = idORclass1; //给dragMing的idORclass1赋值
  5     this.idORclass2 = idORclass2; //给dragMing的idORclass2赋值
  6     this.deltaX = 0;
  7     this.deltaY = 0;
  8 
  9     function dragStart(dragEvent) {
 10         obj.deltaX = dragEvent.clientX - $(obj.idORclass2).offset().left;
 11         obj.deltaY = dragEvent.clientY - $(obj.idORclass2).offset().top;
 12         $(document).bind("mousemove", dragMove);
 13         $(document).bind("mouseup", dragStop);
 14         dragEvent.preventDefault();
 15 
 16     }
 17     function dragMove(dragEvent) {
 18         $(obj.idORclass2).css({
 19             "left": (dragEvent.clientX - obj.deltaX) + "px",
 20             "top": (dragEvent.clientY - obj.deltaY) + "px"
 21         })
 22         dragEvent.preventDefault();
 23 
 24     }
 25     function dragStop() {
 26         $(document).unbind("mousemove", dragMove);
 27         $(document).unbind("mouseup", dragStop);
 28 
 29     }
 30 
 31     $(document).ready(function () {
 32         $(obj.idORclass1).bind("mousedown", dragStart);
 33 
 34     })
 35 }
 36 
 37 
 38 
 39 //绘制键盘
 40 function drawKeyboard(type) {
 41     $("#keyboardNum").empty();
 42     $("#keyboardLetterQ").empty();
 43     $("#keyboardLetterA").empty();
 44     $("#keyboardLetterZ").empty();
 45     $("#keyboardSpaceBar").empty();
 46 
 47     if (type == "lower") {
 48         var keyboardNum = { "192": "`", "49": "1", "50": "2", "51": "3", "52": "4", "53": "5", "54": "6", "55": "7", "56": "8", "57": "9", "48": "0", "189": "-", "187": "=", "8": "Backspace" };
 49         var keyboardLetterQ = { "81": "q", "87": "w", "69": "e", "82": "r", "84": "t", "89": "y", "85": "u", "73": "i", "79": "o", "80": "p", "219": "[", "221": "]" };
 50         var keyboardLetterA = { "20": "Caps Lock", "65": "a", "83": "s", "68": "d", "70": "f", "71": "g", "72": "h", "74": "j", "75": "k", "76": "l", "186": ";", "222": "'", "220": "\\" };
 51         var keyboardLetterZ = { "16": "Shift", "90": "z", "88": "x", "67": "c", "86": "v", "66": "b", "78": "n", "77": "m", "188": ",", "190": ".", "191": "/" };
 52         var keyboardSpaceBar = { "32": "Space", "": "Tim" };
 53         var key = "";
 54     }
 55     else {
 56         var keyboardNum = { "192": "~", "49": "!", "50": "@", "51": "#", "52": "$", "53": "%", "54": "^", "55": "&", "56": "*", "57": "(", "48": ")", "189": "_", "187": "+", "8": "Backspace" };
 57         var keyboardLetterQ = { "81": "Q", "87": "W", "69": "E", "82": "R", "84": "T", "89": "Y", "85": "U", "73": "I", "79": "O", "80": "p", "219": "{", "221": "}" };
 58         var keyboardLetterA = { "20": "Caps Lock", "65": "A", "83": "S", "68": "D", "70": "F", "71": "G", "72": "H", "74": "J", "75": "K", "76": "L", "186": ":", "222": "\"", "220": "|" };
 59         var keyboardLetterZ = { "16": "Shift", "90": "Z", "88": "X", "67": "C", "86": "V", "66": "B", "78": "N", "77": "M", "188": "<", "190": ">", "191": "?" };
 60         var keyboardSpaceBar = { "32": "Space", "": "Tim" };
 61         var key = "";
 62     }
 63     $.each(keyboardNum, function (key, value) {
 64         if (value != "Backspace") {
 65             key = $('<div class="simpleKey" name="key" key="' + key + '" value="' + value + '">' + value + '</div>');
 66             $("#keyboardNum").append(key);
 67         }
 68         else {
 69             key = $('<div class="backspaceKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 70             $("#keyboardNum").append(key);
 71         }
 72     });
 73 
 74     $.each(keyboardLetterQ, function (key, value) {
 75         key = $('<div class="simpleKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 76         $("#keyboardLetterQ").append(key);
 77     });
 78 
 79     $.each(keyboardLetterA, function (key, value) {
 80         if (value != "Caps Lock") {
 81             key = $('<div class="simpleKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 82             $("#keyboardLetterA").append(key);
 83         }
 84         else {
 85             key = $('<div class="capslockKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 86             $("#keyboardLetterA").append(key);
 87         }
 88     });
 89 
 90     $.each(keyboardLetterZ, function (key, value) {
 91         if (value != "Shift") {
 92             key = $('<div class="simpleKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 93             $("#keyboardLetterA").append(key);
 94         }
 95         else {
 96             key = $('<div class="shiftKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
 97             $("#keyboardLetterA").append(key);
 98         }
 99     });
100 
101     $.each(keyboardSpaceBar, function (key, value) {
102         if (value != "Space") {
103             key = $('<div class="simpleKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
104             $("#keyboardSpaceBar").append(key);
105         }
106         else {
107             key = $('<div class="spaceKey" name="key"  key="' + key + '" value="' + value + '">' + value + '</div>');
108             $("#keyboardSpaceBar").append(key);
109         }
110     });
111 
112     addMouseClickEvent();
113 
114 
115 }
116 
117 //监听鼠标点击事件
118 function addMouseClickEvent() {
119     $("#close").click(function () {
120         closeKeyboard()
121     });
122 
123     $("div[name='key']").hover(function () {
124         $(this).css("background-color", "Gray");
125     }, function () {
126         $(this).css("background-color", "White");
127     }).click(function () {
128         var thisValue = $(this).attr("value");
129         var ID = $("#state").val();
130         switch (thisValue) {
131             case "": //"
132                 $("#" + ID).val($("#" + ID).val() + "\"");
133                 if ($("#shift").attr("checked") == true) {
134                     if ($("#capsLock").attr("checked") != true) {
135                         drawKeyboard("lower");
136                     }
137                     $("#shift").attr("checked", false);
138                 }
139                 break;
140             case "Shift":
141                 $("#shift").attr("checked", $("#shift").attr("checked") == true ? false : true);
142                 if ($("#shift").attr("checked") == true) {
143                     drawKeyboard("upper")
144                 }
145                 else {
146                     if ($("#capsLock").attr("checked") != true) {
147                         drawKeyboard("lower");
148                     }
149                 }
150                 break;
151             case "Caps Lock":
152                 $("#capsLock").attr("checked", $("#capsLock").attr("checked") == true ? false : true);
153                 $("#capsLock").attr("checked") == true ? drawKeyboard("upper") : drawKeyboard("lower");
154                 $("#shift").attr("checked", false)
155                 break;
156             case "Space":
157                 $("#" + ID).val($("#" + ID).val() + " ");
158                 break;
159             case "Backspace":
160                 $("#" + ID).val($("#" + ID).val().substring(0, $("#" + ID).val().length - 1));
161                 break;
162             default:
163                 $("#" + ID).val($("#" + ID).val() + thisValue);
164                 if ($("#shift").attr("checked") == true) {
165                     if ($("#capsLock").attr("checked") != true) {
166                         drawKeyboard("lower");
167                     }
168                     $("#shift").attr("checked", false);
169                 }
170 
171                 break;
172         }
173         $("#" + ID).focus();
174     });
175 }
176 
177 
178 //监听键盘事件
179 function addKeydownEvent() {
180     $("html").keydown(function (event) {
181         var realkey = String.fromCharCode(event.keyCode);
182 
183         //特殊键
184         if (event.keyCode == 32) { realkey = "Space" }
185         if (event.keyCode == 13) { realkey = "Enter" }
186         if (event.keyCode == 27) { realkey = " Esc" }
187         if (event.keyCode == 16) {
188             realkey = "Shift";
189             $("#shift").attr("checked", $("#shift").attr("checked") == true ? false : true);
190             if ($("#shift").attr("checked") == true) {
191                 drawKeyboard("upper")
192             }
193             else {
194                 if ($("#capsLock").attr("checked") != true) {
195                     drawKeyboard("lower");
196                 }
197             }
198         }
199         if (event.keyCode == 17) { realkey = " Ctrl" }
200         if (event.keyCode == 18) { realkey = "Alt" }
201         if (event.keyCode == 8) { realkey = "Backspace" }
202         if (event.keyCode == 20) { realkey = "Caps Lock"; $("#capsLock").attr("checked", $("#capsLock").attr("checked") == true ? false : true); $("#capsLock").attr("checked") == true ? drawKeyboard("upper") : drawKeyboard("lower"); }
203 
204 
205         $("div[name='key']").css("background-color", "White")
206         $("div[key=" + event.keyCode + "]").css("background-color", "Gray");
207 
208         //如果按了shif再按其他键并且这个键不是shif键盘变回小写
209         //如果capsLock选中了键盘就不用变回小写
210         if ($("#shift").attr("checked") == true && event.keyCode != 16) {
211             if ($("#capsLock").attr("checked") != true) {
212                 drawKeyboard("lower");
213             }
214             $("#shift").attr("checked", false);
215         }
216 
217     });
218 }
219 
220 //打开键盘
221 function openKeyboard(ID) {
222     $("#keyboard").css("visibility", "visible");
223     $("#state").val(ID);
224 }
225 
226 //关闭键盘
227 function closeKeyboard() {
228     $("#keyboard").css("visibility", "hidden")
229 }
230 
231 
232 $(function () {
233     var divKeyBoard = '<div id="keyboard" class="keyboard"><div id="keyboardHead"><div><input id="shift" type="checkbox"/>Shift</div><div><input id="capsLock" type="checkbox"/>Caps Lock</div><div id="close" style="border:1px solid black; float:right; width:20px; height:20px; cursor:pointer;"><img src="/Image/close.gif" style=" width:20px; height:20px"/></div></div><div id="keyboardNum"></div><div id="keyboardLetterQ"></div><div id="keyboardLetterA"></div><div id="keyboardLetterZ"></div><div id="keyboardSpaceBar"></div></div>';
234     $("body").append(divKeyBoard);
235     drawKeyboard("lower");
236     addKeydownEvent();
237     $("#keyboard").css("visibility", "hidden");
238     var drag = new dragMing("#keyboard", "#keyboard");
239 
240 })
241 
242       
1 .keyboard
2 {
3     width:800px;
4     height:300px;
5     text-align:center;
6     position:absolute;
7 }
8
9 .keyboard div
10 {
11     height:50px;
12 line-height:50px
13      float:left
14 }
15
16 #keyboardHead
17 {
18 width:800px;
19     position:relative;
20 }
21
22 #keyboardLetterQ
23 {
24     width:800px;
25     position:relative;
26     left:75px;
27 }
28
29 #keyboardSpaceBar 30 {
31     width:800px;
32     position:relative;
33     left:200px;
34     top:52px;
35 }
36
37 .simpleKey
38 {
39     width:50px;
40     border:1px solid black;    
41 }
42
43 .enterKey
44 {
45     width:100px;
46     height:100px;
47     border:1px solid black;        
48 }
49
50 .shiftKey
51 {
52     width:115px;
53     border:1px solid black;   
54 }
55
56 .backspaceKey
57 {
58     width:120px;
59     border:1px solid black;
60 }
61
62 .capslockKey
63 {
64     width:90px;
65     border:1px solid black;
66 }
67
68 .spaceKey
69 {
70     width:300px;
71     border:1px solid black;   
72 }
73
74 .keyboard div[name="key"]:hover{
75     background: Gray;
76 }
77
78 .keyboard div[name="key"]
79 {
80     cursor:pointer;
81 }

 

posted @ 2012-07-06 15:38 chen11-1 阅读(2499) | 评论 (1)编辑 收藏

正则表达式汇总

"^\d+$"  //非负整数(正整数 + 0
"^[0-9]*[1-9][0-9]*$"  //正整数
"^((-\d+)|(0+))$"  //非正整数(负整数 + 0
"^-[0-9]*[1-9][0-9]*$"  //负整数
"^-?\d+$"    //整数
"^\d+(\.\d+)?$"  //非负浮点数(正浮点数 + 0
"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$"  //正浮点数
"^((-\d+(\.\d+)?)|(0+(\.0+)?))$"  //非正浮点数(负浮点数 + 0
"^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$"  //负浮点数
"^(-?\d+)(\.\d+)?$"  //浮点数
"^[A-Za-z]+$"  //26个英文字母组成的字符串
"^[A-Z]+$"  //26个英文字母的大写组成的字符串
"^[a-z]+$"  //26个英文字母的小写组成的字符串
"^[A-Za-z0-9]+$"  //由数字和26个英文字母组成的字符串
"^\w+$"  //由数字、26个英文字母或者下划线组成的字符串
"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$"    //email地址
"^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$"  //url

整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
只能输入数字:"^[0-9]*$"
只能输入n位的数字:"^\d{n}$"
只能输入至少n位的数字:"^\d{n,}$"
只能输入m~n位的数字:。"^\d{m,n}$"
只能输入零和非零开头的数字:"^(0|[1-9][0-9]*)$"
只能输入有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"
只能输入有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"
只能输入非零的正整数:"^\+?[1-9][0-9]*$"
只能输入非零的负整数:"^\-[1-9][]0-9"*$
只能输入长度为3的字符:"^.{3}$"
只能输入由26个英文字母组成的字符串:"^[A-Za-z]+$"
只能输入由26个大写英文字母组成的字符串:"^[A-Z]+$"
只能输入由26个小写英文字母组成的字符串:"^[a-z]+$"
只能输入由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"
只能输入由数字、26个英文字母或者下划线组成的字符串:"^\w+$"
验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,tb只能包含字符、数字和下划线。
验证是否含有^%&'',;=?$\"等字符:"[^%&'',;=?$\x22]+"
只能输入汉字:"^[\u4e00-\u9fa5]{0,}$"
验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"
验证InternetURL"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"
验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"正确格式为:"XXX-XXXXXXX""XXXX-XXXXXXXX""XXX-XXXXXXX""XXX-XXXXXXXX""XXXXXXX""XXXXXXXX"
验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"
验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01""09""1""12"
验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01""09""1""31"。整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
只能输入数字:"^[0-9]*$"
只能输入n位的数字:"^\d{n}$"
只能输入至少n位的数字:"^\d{n,}$"
只能输入m~n位的数字:。"^\d{m,n}$"
只能输入零和非零开头的数字:"^(0|[1-9][0-9]*)$"
只能输入有两位小数的正实数:"^[0-9]+(.[0-9]{2})?$"
只能输入有1~3位小数的正实数:"^[0-9]+(.[0-9]{1,3})?$"
只能输入非零的正整数:"^\+?[1-9][0-9]*$"
只能输入非零的负整数:"^\-[1-9][]0-9"*$
只能输入长度为3的字符:"^.{3}$"
只能输入由26个英文字母组成的字符串:"^[A-Za-z]+$"
只能输入由26个大写英文字母组成的字符串:"^[A-Z]+$"
只能输入由26个小写英文字母组成的字符串:"^[a-z]+$"
只能输入由数字和26个英文字母组成的字符串:"^[A-Za-z0-9]+$"
只能输入由数字、26个英文字母或者下划线组成的字符串:"^\w+$"
验证用户密码:"^[a-zA-Z]\w{5,17}$"正确格式为:以字母开头,长度在6~18之间,只能包含字符、数字和下划线。
验证是否含有^%&'',;=?$\"等字符:"[^%&'',;=?$\x22]+"
只能输入汉字:"^[\u4e00-\u9fa5]{0,}$"
验证Email地址:"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"
验证InternetURL"^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$"
验证电话号码:"^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$"正确格式为:"XXX-XXXXXXX""XXXX-XXXXXXXX""XXX-XXXXXXX""XXX-XXXXXXXX""XXXXXXX""XXXXXXXX"
验证身份证号(15位或18位数字):"^\d{15}|\d{18}$"
验证一年的12个月:"^(0?[1-9]|1[0-2])$"正确格式为:"01""09""1""12"
验证一个月的31天:"^((0?[1-9])|((1|2)[0-9])|30|31)$"正确格式为;"01""09""1""31"

posted @ 2012-07-05 13:26 chen11-1 阅读(1096) | 评论 (1)编辑 收藏

如何使自己的程序只运行一次

我介绍两个主流的方法。

方法一:使用Mutex来进行

1. 首先要添加如下的namespace:

using System.Threading;


2. 修改系统Main函数,大致如下:

bool bCreatedNew;

//Create a new mutex using specific mutex name
Mutex m =new Mutex( false, "myUniqueName", out bCreatedNew );
if( bCreatedNew )
Application.Run(new yourFormName());

如上面编码就可以了,要注意的一点是,在给Mutex起名字的时候,不要太简单,以tb防止和其他程序的Mutex重复,从而达不到所预想的效果。


方法二:使用Process来进行

1. 首先要添加如下的namespace:

using System.Diagnostics;
using System.Reflection;

2. 添加如下函数:

public static Process RunningInstance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);

//Loop through the running processes in with the same name
foreach (Process process in processes)
{
//Ignore the current process
if (process.Id != current.Id)
{
//Make sure that the process is running from the exe file.
if (Assembly.GetExecutingAssembly().Location.Replace("/", "//") == current.MainModule.FileName)
{
//Return the other process instance.
return process;
}
}
}

//No other instance was found, return null.
return null;
}

3. 修改系统Main函数,大致如下:

if( RunningInstance() == null )
Application.Run(new yourFormName());

如上面编码就可以了,要注意的一点是,在判断进程模块文件名是否相等这部分的代码,是可选的。如果当前的程序在文件系统中只存在一个的话,以上的方法是可以的;否则不要删除这部分的代码。


对比两种方法,就效率和简便性来说,前一种方法是最好的,也是我比较喜欢的;后一种方法,速度比较慢,其次通过ProcessName去系统中查寻,有可能查出来的Process并不是我想要得,虽说在后面加了文件目录判断,但是其含有潜在的问题(前面已经说出来)。不过,第一种方法也有缺陷,就是扩展性操作不方便,例如:让程序只运行一次,如果程序已经运行,把它弹出并显示到最前面。对于此,后一种方法就很有优势了。

 

posted @ 2012-07-05 13:25 chen11-1 阅读(761) | 评论 (0)编辑 收藏