【转】笑着学习笔记iBatis入门

根据官网上的开发指南,按照个人使用习惯,频度等整理的,没啥特别的东西。

笑着--胖胖兰学iBatis

一,       iBatis简介

iBatis是一种数据库持续层的ORM框架。使用简单的XML配置文件将Java Bean映射成PreparedStatement的输入参数和ResultSet结果集

 

在不是很复杂的情况下,我们 甚至可以使用HashMap而不是Java Bean来映射PreparedStatement的输入参 数和ResultSet结果集,从而节省开发时间与成本。 或者,我们可以使用一些辅助工具来帮助我们通过XML配置生成对应的Java Bean.

 

 

 

 

 

 

 

 

 

二,       iBatis工作原理,流程

iBatis使用简单的XML描述文件将Java BeanMap实现和基本数据类型的包装类(StringInteger等)映射成JDBCPreparedStatement的输入参数 和ResultSet结果集。具体工作流程如下:

 

1.在sqlMapConfigXML配置文件中进行数据库连接的配 置,XML配置文件名称任意,比如sql-map-config.xml

2sqlMapConfigXML配置文件中引用sqlMapXML配置文件

<sqlMap resource="examples/sqlmap/maps/Person1.xml" />

<sqlMap url="file:///c:/config/Customer.xml " />

3.在sqlMapXML配置文件中进行SQL文的配置,文件名称任意, 比如Person1.xml

4.通过SqlMapClientBuilder生成具体的操作sqlMap配置文件中SQL文的 IBATIS对象SqlMapClient

String resource = "config/ibatis/sql-map-config.xml";                  

Reader reader = Resources.getResourceAsReader (resource);                      

SqlMapClient sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);

5SqlMapClient对象提供函数,函数的参数 对应替换sqlMap配置文件中的id,parameter等等属性,完成SQL文的执 行。具体参照iBatisAPI

public List queryForList(String statementName, Object parameterObject,

int skipResults, int maxResults)

statement:sqlMap配置中id属性,parameterObject:sqlMap配置中parameterXXX属性

 

 

iBatis的工作重点在于配置文件的 做成上,尤其sqlMap配置文件的做成是我们需要重点学习的。

 

 

 

 

 

 

 

 

三,       SqlMapConfig.xml

配置例子如下,各个标签的说 明参考官方文档。比较容易理解,这里不做整理

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig

PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"

"http://www.ibatis.com/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

<properties resource=" examples/sqlmap/maps/SqlMapConfigExample.properties " />

<settings

cacheModelsEnabled="true"

enhancementEnabled="true"

lazyLoadingEnabled="true"

maxRequests="32"

maxSessions="10"

maxTransactions="5"

useStatementNamespaces="false"

/>

<typeAlias alias="order" type="testdomain.Order"/>

<transactionManager type="JDBC" >

<dataSource type="SIMPLE">

<property name="JDBC.Driver" value="${driver}"/>

<property name="JDBC.ConnectionURL" value="${url}"/>

<property name="JDBC.Username" value="${username}"/>

<property name="JDBC.Password" value="${password}"/>

<property name="JDBC.DefaultAutoCommit" value="true" />

<property name="Pool.MaximumActiveConnections" value="10"/><property name="Pool.MaximumIdleConnections" value="5"/>

<property name="Pool.MaximumCheckoutTime" value="120000"/>

<property name="Pool.TimeToWait" value="500"/>

<property name="Pool.PingQuery" value="select 1 from ACCOUNT"/>

<property name="Pool.PingEnabled" value="false"/>

<property name="Pool.PingConnectionsOlderThan" value="1"/>

<property name="Pool.PingConnectionsNotUsedFor" value="1"/>

</dataSource>

</transactionManager>

<sqlMap resource="examples/sqlmap/maps/Person.xml" />

<sqlMap url="file:///c:/config/Customer.xml " />

</sqlMapConfig>

 

以上,主要注意DB连接情报的配置方法,采用 了Java的标准Properties文件。

<properties resource=" examples/sqlmap/maps/SqlMapConfigExample.properties " />

       然后在XML中引用properties文 件中的定义。

           <property name="JDBC.Driver" value="${driver}"/>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四,       SqlMap.xml总体印象

较复杂的例子:

<sqlMap id=”Product”>

<cacheModel id=”productCache” type=”LRU”>

<flushInterval hours=”24”/>

<property name=”size” value=”1000” />

</cacheModel>

<typeAlias alias=”product” type=”com.ibatis.example.Product” />

<parameterMap id=”productParam” class=”product”>

<parameter property=”id”/>

</parameterMap>

<resultMap id=”productResult” class=”product”>

<result property=”id” column=”PRD_ID”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

</resultMap>

<select id=”getProduct” parameterMap=”productParam”

resultMap=”productResult” cacheModel=”product-cache”>

select * from PRODUCT where PRD_ID = ?

</select>

</sqlMap>

 

      以上例子简化版:

<sqlMap id=”Product”>

<select id=”getProduct” parameterClass=” com.ibatis.example.Product”

resultClass=”com.ibatis.example.Product”>

Select

PRD_ID as id,

PRD_DESCRIPTION as description

From PRODUCT

Where PRD_ID = #id#

</select>

</sqlMap>

 

五,       Statement语法,类型,及其属性和特点

1.语法

<statement

 id=”statementName”

[parameterClass=”some.class.Name”]

[resultClass=”some.class.Name”]

[parameterMap=”nameOfParameterMap”]

[resultMap=”nameOfResultMap”]

[cacheModel=”nameOfCache”]

>

select * from PRODUCT where PRD_ID = [?|#propertyName#] order by [$simpleDynamic$]

</statement>

 

[]内 为可选项,不是必须存在的。

 

2.Statement类型,属性,特点

六,       关于映射时输入值的匹配问题

1.     SqlMapClient如何从SqlMap的配置文件中找到对应的SQL

通过类函数的参数statementName与配置文件中statement配置的id相匹配

 

2.     bean中的数据如何映射给SQL文中对应的输入参数

a.     通过参数变量名称与bean中对应属性名称相匹配

<statement id=”statementName” parameterClass=” examples.domain.Product”>

insert into PRODUCT values (#id#, #description#, #price#)

</statement>

 

b.     通过参数变量的出现顺序相 匹配

<parameterMap id=”insert-product-param” class=”com.domain.Product”>

<parameter property=”id”/>

<parameter property=”description”/>

</parameterMap>

<statement id=”insertProduct” parameterMap=”insert-product-param”>

insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);

</statement>

 

3.     map中的数据如何映射给SQL文中对应的输入参数

a.     通过参数变量名称与map中对应key相匹配

<statement id=”statementName” parameterClass=” java.util.Map”>

insert into PRODUCT values (#id#, #description#, #price#)

</statement>

 

HashMap hm = new HashMap();

hm.put(“id”,”value”);

hm.put(“description”,”value”);

hm.put(“price”,”value”);

sqlMapClient.queryForList(“statementName”, hm,…………);

 

 

 

4.     基本数据类型的数据如何映 射给SQL文中对应的输入参数

基本数据类型与参数11,没啥匹配的问题。不过官 方的开发指南中强调参数要写成#value#这种写法,原因不明。

<statement id=”insertProduct” parameter=”java.lang.Integer”>

select * from PRODUCT where PRD_ID = #value#

</statement>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

七,       关于映射时返回值的匹配问题

1.     查询结果如何映射给bean中的对应属性

a.     通过查询结果的字段名称与bean中对应属性名称相匹配

<statement id=”getProduct” resultClass=”com.ibatis.example.Product”>

select

PRD_ID as id,

PRD_DESCRIPTION as description

from PRODUCT

where PRD_ID = #value#

</statement>

 

官 方的开发指南中说这种写法叫做隐式ResultMap

 

b.     指定查询结果的字段名称与bean中对应属性名称的映射关系

<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>

<result property=”id” column=”PRD_ID”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

<result property=”subCode” column=”PRD_SUB_CODE” nullValue=”-999”/>

</resultMap>

 

以上,数据类型通过反射与对 应的属性类型自动匹配,极少数时候可能需要通过resultMapjavaType属性进行设定,参考后面关于Result Map外部形式中各个参数的说明

 

2.     查询结果如何设定给Map

a.     通过查询结果的字段名称与map中对应key相匹配

<resultMap id=”get-product-result” class=”java.util.HashMap”>

<result property=”id” column=”PRD_ID”/>

<result property=”code” column=”PRD_CODE”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

<result property=”suggestedPrice” column=”PRD_SUGGESTED_PRICE”/>

</resultMap>

 

<statement id=”getProductCount” resultClass=”java.util.HashMap”>

select * from PRODUCT

</statement>

 

b.     指定查询结果的字段名称与bean中对应属性名称的映射关系

bean基本相同,参考使用bean作为返回结果的写法

 

3.     查询结果如何映射给基本数 据类型

只能指定一个返回值,名字可 以随便,一般采用value,val

<resultMap id=”get-product-result” class=”java.lang.String”>

<result property=”value” column=”PRD_DESCRIPTION”/>

</resultMap>

 

或 者

<statement id=”getProductCount” resultClass=”java.lang.Integer”>

select count(1) as value

from PRODUCT

</statement>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

八,       关于Parameter Map外部形式中各个参数的说明

<parameterMap id=”parameterMapName” [class=”com.domain.Product”]>

<parameter

property =”propertyName”

[jdbcType=”VARCHAR”]

[javaType=”string”]

[nullValue=”NUMERIC”]

[null=”-9999999”]/>

<parameter …… />

<parameter …… />

</parameterMap>

1.JdbcType

属 性jdbcType用于显式地指定给本属性(property) 赋值的数据库字段的数据类型。对于某些特定的操作,如果不指定字段的数据类型,某些JDBC Driver无 法识别字段的数据类型。

 

正 常情况下,只有当字段可以为NULL时才需要jdbcType属 性。另一需要指定jdbcType属性的情况是字段类型为日期时间类型的情况。因为Java只 有一个Date类型(java.util.Date), 而大多数SQL数据库有多个-通常至少有3种。因此,需要指定字段类型是DATE还是DATETIME。

 

注意!当使用Oracle Driver时,如果没有给可以为NULL的字段指定jdbcType属性,当试图给这些字段赋 值NULL时,会出现“Invalid column type”错误。

 

2.javaType

属 性javaType用于显式地指定被赋值参数Java属 性的类名。正常情况下,这可以通过反射从Java Bean的 属性获得

 

3.nullValue, null

   用 于指定NULL的替换值。就是说,当Java Bean的属性值等于指定值时,相应的字段将赋值NULL。 这个特性允许在应用中给不支持null的数据类型(即intdoublefloat等) 赋值null。当这些数据类型的属性值匹配null值 (即匹配-9999)时,NULL将 代替null值写入数据库。

      一个完整的例子

<parameterMap id=”insert-product-param” class=”com.domain.Product”>

<parameter

property=”id”

jdbcType=”NUMERIC”

javaType=”int”

nullValue=”-9999999”/>

<parameter

property=”description”

jdbcType=”VARCHAR”

nullValue=”NO_ENTRY”/>

</parameterMap>

 

<statement id=”insertProduct” parameterMap=”insert-product-param”>

insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);

</statement>

 

      简化写法

<statement id=”insertProduct” parameterClass=”com.domain.Product”>

insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)

values (#id:NUMERIC#, #description:VARCHAR#);

</statement>

 

或者:

 

<statement id=”insertProduct” parameterClass=”com.domain.Product”>

insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)

values (#id:NUMERIC:-999999#, #description:VARCHAR:NO_ENTRY#);

</statement>

 

 

 

 

九,       Result Map外部形式中各个参数的说明

<resultMap id=”resultMapName” class=”some.domain.Class” [extends=”parent-resultMap”]>

<result property=”propertyName” column=”COLUMN_NAME”

[columnIndex=”1”]

[javaType=”int”]

[jdbcType=”NUMERIC”]

[nullValue=”-999999”]

[select=”someOtherStatement”]

/>

<result ……/>

<result ……/>

<result ……/>

</resultMap>

1. columnIndex

属性columnIndex是可选的,用于改善性能。 属性columnIndex的值是ResultSet中用于赋值Java Bean属性的字段次序号。在99%的应用中,不太可能需要牺牲可读性来换取性能。使用columnIndex,某些JDBC Driver可以大幅提高性能,某些则没有任何效果。

 

2.javaType

属性javaType用于显式地指定被赋值的Java Bean属性的类型。正常情况下,这可以通过反射从Java Bean的属性获得,但对于某些映射 (例如MapXML document),框架不能通过这种方法来 获知。如果没有设置javaType, 同时框架也不能获知类型信息,类型将被假定为Object

 

3.jdbcType,nullValue, null

   Parameter Map

4.select

属 性select用于描述对象之间的关系,并自动地装入复杂类型(即用户定义的类型)属性的数 据。属性select的值必须是另外一个mapped statement元素的名称。在同一个result元 素中定义的数据库字段(column属性)以及property属 性,将被传给相关的mapped statement作为参数。因此,字段的数据类型必须是SQL Map支持的简单数据类型。关于简单数据类型和复杂类型之间映射/关 系的信息,参照后面章节更详细的讨论。

 

具 体参考稍后关于复杂类型属性的例子

十,       复杂类型属性说明

1.关于复杂类型属性的例子

<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>

<result property=”id” column=”PRD_ID”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

<result property=”category” column=”PRD_CAT_IDselect=”getCategory”/>

</resultMap>

<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>

<result property=”id” column=”CAT_ID”/>

<result property=”description” column=”CAT_DESCRIPTION”/>

</resultMap>

<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>

select * from PRODUCT where PRD_ID = #value#

</statement>

<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>

select * from CATEGORY where CAT_ID = #value#

</statement>

 

         避 免N1 Select1:1  

<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>

<result property=”id” column=”PRD_ID”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

<result property=”category.id” column=”CAT_ID” />

<result property=”category.description” column=”CAT_DESCRIPTION” />

</resultMap>

<statement id=”getProduct” parameterClass=”int” resultMap=”get-product-result”>

select *

from PRODUCT, CATEGORY

where PRD_CAT_ID=CAT_ID

and PRD_ID = #value#

</statement>

 

 

2.关于复杂类型级和属性的例子

<resultMap id=”get-category-result” class=”com.ibatis.example.Category”>

<result property=”id” column=”CAT_ID”/>

<result property=”description” column=”CAT_DESCRIPTION”/>

<result property=”productList” column=”CAT_ID” select=” getProductsByCatId”/>

</resultMap>

<resultMap id=”get-product-result” class=”com.ibatis.example.Product”>

<result property=”id” column=”PRD_ID”/>

<result property=”description” column=”PRD_DESCRIPTION”/>

</resultMap>

<statement id=”getCategory” parameterClass=”int” resultMap=”get-category-result”>

select * from CATEGORY where CAT_ID = #value#

</statement>

<statement id=”getProductsByCatId” parameterClass=”int” resultMap=”get-product-result”>

select * from PRODUCT where PRD_CAT_ID = #value#

</statement>

 

         避 免N1 Select1:1  

暂 时无解,在新版本中可能会对应

 

3.组合键值或多个复杂参数属性

<resultMap id=”get-order-result” class=”com.ibatis.example.Order”>

<result property=”id” column=”ORD_ID”/>

<result property=”customerId” column=”ORD_CST_ID”/>

<result property=”payments” column=”{itemId=ORD_ID, custId=ORD_CST_ID}

select=” getOrderPayments”/>

</resultMap>

<statement id=”getOrderPayments” resultMap=”get-payment-result”>

select * from PAYMENT

where PAY_ORD_ID = #itemId#

and PAY_CST_ID = #custId#

</statement>

十一,   在查询statement中指定cacheModel属性

<cacheModel id="product-cache" type ="LRU" readOnly=”true” serialize=”false”>

<flushInterval hours="24"/>

<flushOnExecute statement="insertProduct"/>

<flushOnExecute statement="updateProduct"/>

<flushOnExecute statement="deleteProduct"/>

<property name=”cache-size” value=”1000” />

</cacheModel>

 

<statement id=”getProductList” cacheModel=”product-cache”>

select * from PRODUCT where PRD_CAT_ID = #value#

</statement>

 

1Serializable可读写缓存

正 如您所知道的,只对当前Session有效的缓存对整体应用性能的提高作用有限。Serializable可读写缓存可以提高整体应用(而不仅仅是每个Session)的性能。这种缓存为每一个Session返回缓存对象不同的实例(复本)。因此每一个Session都可以安全修改返回的对象。不同之处在于,通常您希望从缓存中得到同一个对象,但这种情况下得到的是不同的对象。还有,每一个 缓冲在Serializable缓 存的对象都必须是Serializable的。 这意味着不能同时使用Serializable缓存和延迟加载,因为延迟加载代理不是Serializable的。想知道如何把Serializable缓存,延迟加载和联合查询结合起来使用,最好的方法是尝试。要使用Serializable缓 存,设置readOnly=falseserialize=true。缺省情况下,缓存是只读模式,不使用Serializable缓存。只读缓存不需要Serializable

 

 

 

 

 

 

 

 

 

2Type

    a.type=MEMORY

MEMORY cache实现只认识一个<property>元素。这个名为reference-type属性的值必须是STRONGSOFTWEAK三者其一。这三个值分别对应于JVM不同的内存reference类型。

<property name=”reference-type” value=”WEAK” />

 

 

    b.type=LRU

LRU Cache实现用近期最少使用原则来确定如何从Cache中清除对象。

       <property name=”cache-size” value=”1000” />

 

    c.type=FIFO

       FIFO Cache实 现用先进先出原则来确定如何从Cache中清除对象。

<property name=”size” value=”1000” />

 

d.type= OSCACHE  没 用过,没研究过。这个不太懂,哈

OSCACHE Cache实现是OSCache2.0缓存引擎的一个Plugin。它具有高度的可配置性,分布式,高度的灵活性。

 

OSCACHE实现不使用property元素,而是在类路径的根路径中使用标准的oscache.properties文件进行配置。在oscache.properties文件中,您可以配置Cache的算法(和上面讨论的算法很类似),Cache的大小,持久化方法(内存,文件等)和集群方法。

要获得更详细的信息,请参考OSCache文档。OSCache及其文档可以从OpenSymphony网站上获取:

http://www.opensymphony.com/oscache/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

十二,   动态Mapped Statement

<select id="dynamicGetAccountList"

cacheModel="account-cache"

resultMap="account-result"

>

select * from ACCOUNT

<isGreaterThan prepend="and" property="id" compareValue="0">

where ACC_ID = #id#

</isGreaterThan>

order by ACC_LAST_NAME

</select>

上 面的例子中,根据参数beanid属性的不同情况,可创建两个可能的语句。如果参数id大于0,将创建下面的语句:

select * from ACCOUNT where ACC_ID = ?

或 者,如果id参数小于等于0,将创建下面的语句:

select * from ACCOUNT

 

1.二元条件元素

二 元条件元素将一个属性值和一个静态值或另一个属性值比较,如果条件为,元素体的内容将被包括在查询SQL语句中。

 

二元条件元素的属性:

prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)

property - 被比较的属性(必选)

compareProperty - 另一个用于和前者比较的属性(必选或选择compareValue

compareValue - 用于比较的值(必选或选择compareProperty

<isEqual>

比较属性值和静态值或另一个属性值是否相等。

<isNotEqual>

比较属性值和静态值或另一个属性值是否不相等。

<isGreaterThan>

比较属性值是否大于静态值或另一个属性值。

<isGreaterEqual>

比较属性值是否大于等于静态值或另一个属性值。

<isLessThan>

比较属性值是否小于静态值或另一个属性值。

<isLessEqual>

比较属性值是否小于等于静态值或另一个属性值。

例子:

<isLessEqual prepend=”AND” property=”age” compareValue=”18”>

ADOLESCENT = ‘TRUE’

</isLessEqual>

 

2.一元条件元素

一 元条件元素检查属性的状态是否符合特定的条件。

一元条件元素的属性:

prepend - 可被覆盖的SQL语句组成部分,添加在语句 的前面(可选)

property - 被比较的属性(必选)

<isPropertyAvailable>

检查是否存在该属性(存在parameter bean的属性)。

<isNotPropertyAvailable>

检查是否不存在该属性(不存在parameter bean的属性)。

<isNull>

检查属性是否为null

<isNotNull>

检查属性是否不为null

<isEmpty>

检查Collection.size()的值,属性的StringString.valueOf(),是否为null或空(“”size() < 1)。

<isNotEmpty>

检查Collection.size()的值,属性的StringString.valueOf(),是否不为null或 不为空(“”size() > 0)。

例子:

<isNotEmpty prepend=”AND” property=”firstName” >

FIRST_NAME=#firstName#

</isNotEmpty>

 

3其他元素

Parameter Present:这些元素检查参数对象 是否存在。

Parameter Present的属性:

prepend - 可被覆盖的SQL语句组成部分,添加在语 句的前面(可选)

<isParameterPresent>

检 查是否存在参数对象(不为null)。

<isNotParameterPresent>

检 查是否不存在参数对象(参数对象为null)。

例子:

<isNotParameterPresent prepend=”AND”>

EMPLOYEE_TYPE = ‘DEFAULT’

</isNotParameterPresent>

 

4Iterate:这属性遍历整个集合,并为List集 合中的元素重复元素体的内容。

Iterate的属性:

prepend - 可被覆盖的SQL语句组成部分,添加在语句的前面(可选)

property - 类型为java.util.List的用于遍历的元素(必选)

open - 整个遍历内容体开始的字符串,用于定义括号(可选)

close -整个遍历内容体结束的字符串,用于定义括号(可选)

conjunction - 每次遍历内容之间的字符串,用于定义ANDOR(可选)

<iterate>

遍历类型为java.util.List的元素。

例子:

<iterate prepend=”AND” property=”userNameList”

open=”(” close=”)” conjunction=”OR”>

username=#userNameList[]#

</iterate>

注意:使用<iterate>时,在List元素名后面包括方括号[]非常重要,方括号[]将对象标记为List,以防解析器简单地将List输出成String

 

一,       注 意事项

1.关于特殊字符

因为SQL语句是嵌在XML文档中的, 因此有些特殊的字符不能直接使用,例如大于号和小于号(<>)。幸运的 是,解决的办法很简单,只需将包含特殊字符的SQL语句放在XMLCDATA区里面就可 以了。例 如:


<statement id="getPersonsByAge" parameterClass=”int” resultClass="examples.domain.Person">

<![CDATA[

SELECT *

FROM PERSON

WHERE AGE > #value#

]]>

</statement>

 

 

 

2.关于多个SqlMap.xml 中id冲突问题

问题描述:

sql-map-config中引用多个sqlmap.xml,多个sqlmap.xml中存在相同的statementname的时候,ibatis如何判断当前我准备使用哪 一个sqlmap.xml中的statementname对应的sql语句

      

       解决方法:

SqlMapConfig.xml中的settings标签中存在一个属性useStatementNamespaces当这个设定 为true的时候,那 么iBatis通过SqlMap.xmlnamespace.id来查找对应 的sqlid,从而避免 这个问题。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

二,       其他

1.自动生成的主健

<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">

<selectKey resultClass="int" keyProperty="id" >

SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL

</selectKey>

insert into PRODUCT (PRD_ID,PRD_DESCRIPTION)

values (#id#,#description#)

</insert>

 

<!— Microsoft SQL Server IDENTITY Column Example -->

<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">

insert into PRODUCT (PRD_DESCRIPTION)

values (#description#)

<selectKey resultClass="int" keyProperty="id" >

SELECT @@IDENTITY AS ID

</selectKey>

</insert>

 

2.调用存储过程

<parameterMap id="swapParameters" class="map" >

<parameter property="email1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

<parameter property="email2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/>

</parameterMap>

<procedure id="swapEmailAddresses" parameterMap="swapParameters" >

{call swap_email_address (?, ?)}

</procedure> SELECT @@IDENTITY AS ID

 

十五,   Sample

1.java类,生成iBatis数据库操作对象

public class MyAppSqlConfig {

private static final SqlMapClient sqlMap;

static {

try {

String resource = “com/ibatis/example/sql-map-config.xml”;

Reader reader = Resources.getResourceAsReader (resource);

sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException (“”);

}

}

public static getSqlMapInstance () {

return sqlMap;

}

}

 

2.配置文件:

Continue reading 【转】笑着学习笔记iBatis入门

【转】Instruction about how to load “.csv” file into MySQL

1.       Change the configuration file of MySQL. (MySQL can’t identify some incorrect strings of the .csv file we want to load, so that we have to change the setting of MySQL)


l         Open your "my.ini" file within the MySQL installation directory, and look for the text "sql-mode". Find:


l         Add the following code to “my.ini” file



2.       Download the .csv files and unzip it:


File NameDownload Link
1. GeoIPCountryCSV.zip (It contains one database file)http://www.maxmind.com/app/geoip_country
2. GeoLiteCity_20100601.zip (It contains two database files)http://www.maxmind.com/app/geolitecity



Note: Actually, there is one database named GeoLiteRegion, which doesn’t have free version right now. So we didn’t choose to use it.




3.       Use MySQL Workbench to load the unzipped .csv files to database


l         According to the number of columns of the .csv file, we need to create the corresponding table and give a name to each column.

l         After the table has been established, we can use the following command to automatically load the data :


If you guys are still confusing about the code above, you check the following websites out:

http://forums.mysql.com/read.php?79,219901,219901
http://www.tech-recipes.com/rx/2345/import_csv_file_directly_into_mysql/

建议使用dat文件读取。

Continue reading 【转】Instruction about how to load “.csv” file into MySQL

extjs grid使用总结

版本:3.2.1,主要使用在BannerAdmin上:

1:使用store要注意:
一般使用record.beginEdit, set,endEdit,---(update event)-->commit/reject的步骤来更新/还原数据,直接修改record的data的值不会触发update等事件,估计这样的话reject也不会起作用。
2:EditGrid的默认selmode是CellSelectModel.CheckColumn是在ux包里面的。发现直接修改CheckColumn绑定的record.data后,会反映在CheckColumn上。
3:EditGrid要支持拖拽的话,还要改代码:[见cm.js]
Ext.override(Ext.grid.GridPanel, {
               getDragDropText : function() {
                   var sm = this.selModel;

                   var count;
                   if (sm instanceof Ext.grid.CellSelectionModel) {
                       count = 1;
                   } else {
                       count = sm.getCount();
                   }
                   return String.format(this.ddText, count, count == 1
                                   ? ''
                                   : 's');
               }
           });
   Ext.override(Ext.grid.GridDragZone, {
               getDragData : function(e) {
                   var t = Ext.lib.Event.getTarget(e);
                   var rowIndex = this.view.findRowIndex(t);
                   var cellIndex = this.view.findCellIndex(t);

                   if (rowIndex !== false) {
                       var sm = this.grid.selModel;

                       // RowSelectionModel
                       if (sm instanceof Ext.grid.RowSelectionModel) {
                           if (!sm.isSelected(rowIndex) || e.hasModifier()) {
                               sm.handleMouseDown(this.grid, rowIndex, e);
                           }
                           return {
                               grid : this.grid,
                               ddel : this.ddel,
                               rowIndex : rowIndex,
                               selections : sm.getSelections()
                           };
                       }

                       // CellSelectionModel
                       if (sm instanceof Ext.grid.CellSelectionModel) {
                           sel = sm.getSelectedCell();

                           rowAlreadySelected = sel && sel[0] == rowIndex;

                           if (!rowAlreadySelected || e.hasModifier()) {
                               sm.handleMouseDown(this.grid, rowIndex,
                                       cellIndex, e);
                           }

                           store = this.grid.getStore();
                           sel = sm.getSelectedCell();
                           if (sel)
                               return {
                                   grid : this.grid,
                                   ddel : this.ddel,
                                   rowIndex : rowIndex,
                                   selections : [store.getAt(sel[0])]
                               };
                           else
                               return {
                                   grid : this.grid,
                                   ddel : this.ddel,
                                   rowIndex : rowIndex,
                                   selections : []
                               };
                       }
                   }
                   return false;

               }
           });

Continue reading extjs grid使用总结

gzip系列

ie5就开始支持gzip了[待考证,反正ie6是支持的]

传统的JS压缩(删除注释,删除多余空格等)提供的压缩率有时还是不尽不意, 幸亏现在的浏览器都支持压缩传输(通过设置http header的Content-Encoding=gzip),可以通过服务器的配置(如apache)为你的js提供压缩传输,或是appfuse中使 用的GZipFilter使tomcat也提供这种能力

现在的问题是这种动态的压缩会导致服务器CPU占用率过高,现在我想到的解决辨法是通过提供静态压缩(就是将js预先通过gzip.exe压缩 好)

一.下面描述在tomcat中的应用
httpServletResponse.setHeader("Content-Encoding", "gzip");//主要是这一句,但是如果仅仅加个header,让容器自己输出的话,没有起作用,返回流的头部还是没有gzip信息,所以还得自己负责返回文件流,这个又加重了服务端cpu,但是减少了带宽。
对于chrome,还要返回Content-Type,否则会被忽略
httpServletResponse.setHeader("Content-Type", "text/plain");//这只对于非图片类型

对于safari,直接.gz为扩展名的它处理有问题,要改成别的如.jgz

1.将prototype.js通过gzip.exe压缩保存成prototype.gzjs
2.设置header,我编写了一个简单的AddHeadersFilter来将所有以gzjs结尾的文件增加设置header Content-Encoding=gzip
web.xml中的配置

Java 代码
  1. <filter> 
  2.     <filter-name>AddHeaderFilter</filter-name> 
  3.     <filter-class
  4.         badqiu.web.filter.AddHeaderFilter 
  5.     </filter-class
  6.     <init-param> 
  7.         <param-name>headers</param-name> 
  8.         <param-value>Content-Encoding=gzip</param-value> 
  9.     </init-param> 
  10. </filter> 
  11.  
  12. <filter-mapping> 
  13.     <filter-name>AddHeaderFilter</filter-name> 
  14.     <url-pattern>*.gzjs</url-pattern> 
  15. </filter-mapping> 

测试prototype.js是否正常的代码

Java 代码
  1. <html> 
  2. <head> 
  3. <!-- type="text/javascript"不可少,有些浏览器缺少这个不能运行,具体已经忘记 了 --> 
  4. <script src="prototype.gzjs" type="text/javascript"></script> 
  5. </head> 
  6. <body> 
  7.     <input id="username" name="username" value="badqiu"/><br /> 
  8.     <input id="email" value="[email protected]"/> 
  9. <script> 
  10.     <!-- 测 试prototype的方法是否正常--> 
  11.     alert($F('username')) 
  12. </script> 
  13. </body> 
  14. </html> 

在Apache httpd中可以直接通过在httpd.conf增加AddEncoding x-gzip .gzjs来映射.gzjs文件的header

二.相关压缩率数据
1. prototype.js 1.5.0_rc0原始大小56KB,未经任何处理直接使用gzip压缩为12KB,总压缩率79%
2. 通过js压缩工具压缩过的protytype.js为20KB,使用gzip压缩为10KB,总压缩率为83%
3. 实际项目中的多个js合并成的文件 439KB,直接通过gzip压缩为85KB,总压缩率81%
4. 439KB经过js压缩为165KB,再经过gzip压缩为65KB,总压缩率86%

基本上你都可以忽略js压缩工具的压缩率,直接使用gzip压缩

gzip下载地址 http://www.gzip.org
tomcat的压缩配置示例下载地址: http://www.blogjava.net/Files/badqiu/gziptest.rar

==================================================================

Wordpress之终极Gzip兼容IE6

为了这Gzip我可没有少费功夫啊,之前我已经写过三篇文章《开启GZIP,提速 Wordpress》《给Wordpress开启Gzip 功能》《完美启用Gzip压缩JS、 CSS》,三篇文章三个方法,各有特色,一直以来我是将这三个Gzip结合使用的,但是始终没有达到我期望的最佳效果,因为启用后就不得不放弃 IE6的用户。这一bug让我在使用Gzip时很尴尬,之后我也试过给浏览器设置白名单,希望IE6跳过Gzip,最后以失败告终。不过今天终于让我找到 一个可以兼顾IE6的开启Gzip的方法,让我的Wordpress载入速度又上了一个台阶。特地将此方法转载来和大家分享,因为原文是繁体,我就简化了 一下:

1、下载gzip.zip, 解压后上传至网站根目录。

2、修改根目录的.htaccess,增加gz的识别支援及网址改写:

#識辨gz檔案的支援
<Files *.js.gz>
  AddEncoding gzip .js
  ForceType application/x-javascript
</Files>
<Files *.css.gz>
  AddEncoding gzip .css
  ForceType text/css
</Files>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

#wordpress靜態 網址,如果沒有使用,就略過
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

#讀到css和js檔,就 重導至gzip.php
RewriteRule (.*.css$|.*.js$) gzip.php?$1 [L]

#讀到.css,判斷如果 瀏覽器支援gzip且.css.gz檔存在,就進行重導
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*).css $1.css.gz [L,QSA]

#讀到.js,判斷如果瀏 覽器支援gzip且.js.gz檔存在,就進行重導
RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*).js $1.js.gz [L,QSA]
</IfModule>

3、浏览自己的网站,让CSS和JS产生gz档,并将相应的.gz文件权限设置为777。

4、最后,再次改写.htaccess,把以下代码注释掉或删除。

RewriteRule (.*.css$|.*.js$) gzip.php?$1 [L]

完毕。此Gzip方法是至今我最满意的,效果最显著的,强烈推荐。不过在修改CSS和JS后,还要还原那句代码并重做第3、4步。所以该方法稍 有些复杂,比较适合像我一样喜欢折腾Wordpress的童鞋们。

给 Wordpress 开启 Gzip 功能

曾经我为了给 Wordpress 开启 Gzip 功能真是煞费苦心,但最终还是成功了,并写下日志《开启GZIP,提速 Wordpress》分享给大家。据willin的反馈,他使用该方法感觉 不错,但是我自己用这个方法却发现主题在IE6下裸奔了,至今没有找到问题的根源,之后只好弃用了该方法。郁闷~

但是我一直没有放弃对开启 Gzip 的追求,皇天不负有心人,终于让我找到了两个给 Wordpress 开启 Gzip 的方法,且比在《开启GZIP,提速 Wordpress》中提到的方法更简单,更实用,也没有在 IE6 下裸奔的现象,值得推荐。至于什么是 Gzip、如何检测网站是否开启 Gzip 功能等问题本文就不多作熬述了,欲知答案请移步《开启GZIP,提速 Wordpress》。下面马上切入正题:

方法一:

gzippy 插件。

这对于懒人或者菜菜们来说是个福音和首选,这个插件解压后也不足2K,可谓短小精悍,且上传安装之后也无需另外设置,很方便实用。

下载地址:http://wordpress.org/extend/plugins/gzippy/

方法二:

定义 php.ini。

新建一个名为 php.ini 的文件,输入以下内容,保存并上传至网站根目录即可。

output_buffering = Off
output_handler =
zlib.output_compression = On

因为本人对插件有抵触情绪,能 用代码搞定的就绝不用插件解决!所以我现在用的是方法二,压缩率达到73%,还是比较让人满意的。现在我的 YSlow 成绩又回到了 Grade B,真要感谢 Gzip 的压缩功能啊.


完美启用Gzip压缩JS、CSS

这已经是我自《开启GZIP,提速 Wordpress》《给Wordpress开启Gzip 功能》两篇文章后第三次介绍Wordpress中的Gzip功能,而这三篇中所涉及的Gzip又各有不同,可以结合实用。在《给Wordpress开启Gzip 功能》中提到的Gzip功能很简单,但是只对Wordpress输出的html进行压缩,所以效果有限。而这篇文章提及的Gzip功能将对 Wordpress中的重头戏JS和CSS进行压缩,这将使Wordpress的载入速度达到一个质的飞跃!

1、把模板目录下的style.css复制一份出来,命名为style.css.php,接着在style.css.php顶部加入这句:

<?php if(extension_loaded('zlib')) {ob_start('ob_gzhandler');} header("Content-type: text/css"); ?>

在最后加上下面代码:

<?php if(extension_loaded('zlib')) {ob_end_flush();} ?>

2、按照下面的方式修改header.php中的css连接

原来的:

<link rel="stylesheet" type="text/css" media="screen" href="/style.css"/>

修改后的:

<link rel="stylesheet" type="text/css" media="screen" href="/style.css.php"/>

这样,你的CSS就被Gzip压缩了,同样的方法可以压缩你的JS。只是在JS的顶部加入的是如下代码:

<?php if ( extension_loaded('zlib') ) {ob_start('ob_gzhandler');} header("Content-Type: text/javascript"); ?>

说说我自己使用Gzip压缩后的效果吧,在《开启GZIP,提速 Wordpress》中我就提到,根据YSlow的要求,其中Compress components with gzip这项,我的得分仅为F,严重地影响了小站的整体评级。开启Gzip之后,我再用YSlow去检测,现在我的得分是Grade A!再贴个图让各位有个直观的理解,看看我的JS和CSS减了多少肥吧。JS和CSS的大小从102.1K直降到33.8K,足足减少了三分之二的文件大 小,如果你有用prototype.js这样的大型JS,那压缩之后的效果就更可观了。!

开启此Gzip功能比《给Wordpress开启Gzip 功能》中提到的Gzip要复杂的多,因为要对所有JS和CSS一一修改,且要找到并修改调用该JS和CSS的代码,如果你还是刚刚踏入 Wordpress大家庭的小菜菜,那我还是建议你用《给Wordpress开启Gzip 功能》中的插件或php.ini来实现Gzip。

最后友情提醒一下,因为使用此Gzip,需要修改比较多的文件和代码,所以记得备份哟,如果做错也好有个挽回的余地。

开启GZIP,提速Wordpress

今天我用FireFox的YSlow组件对小站进行了评测,结果仅为Grade C,实在是差强人意。虽然自认为本站的速度已经算是很不错了,话说自从换了小张童鞋的息壤主 机后,小站的速度也是突飞猛进,但是为了满足小小的虚容心和完美主义性格,Grade C的结果在我看来是不合格的,我的目标是Grade A!我需要优化、优化、再优化,提速、提速、再提速!

根据YSlow的要求,其中Compress components with gzip这项,我的得分仅为F,严重地影响了小站的整体评级。本文也就重点谈谈如何开启GZIP压缩网页从而给Wordpress提速。

三言两语先简单介绍下GZIP功能,我G来的。GZIP功能可以大幅度地压缩CSS、JS之类的文本型文件,压缩率达60%-90%,挺可观 的。GZIP压缩功能在Wordpress2.3-2.5版本里都是自带的,之后就没有了,我现在用的是Wordpress2.7.1,就更没有这个功能 了。那如何手动开启Wordpress2.7+的GZIP功能呢?

在开始之前,我们先做点准备工作,你也可以用YSlow查一下你的网站评级,然后登陆http://www.whatsmyip.org/mod_gzip_test/检测一下你的网站是否已经开 启了GZIP,等事后可以作个比较。下面是我开启GZIP之前的测试截图。

wange.im

废话到此为止,进入正题。

1.开启GZIP功能。在根目录下的index.php找到

define(’WP_USE_THEMES’, true);

在其后插入如下代码:

if(ereg(’gzip’,$_SERVER['HTTP_ACCEPT_ENCODING'])){//判断浏览器是否支持Gizp
if(substr($_SERVER['REQUEST_URI'],0,10)!=’/download /’)//排除不需要Gzip压缩的目录
ob_start(’ob_gzhandler’);//打开Gzip压缩
}

2.在.htaccess里面加上RewriteRule (.*.css$|.*.js$) gzip.php?$1 [L]

如果你那可怜的主机不支持.htaccess可写,很遗憾,你就不用往下看了。

3.压缩CSS和JS文件。你可以复制以下代码保存为gzip.php,或者点此下载我的gzip.php文件,下载解压之后上传至根目录即可。让所有的CSS和JS文件访问就以相对根目 录的路径以GET变量传递到了gzip.php,交给gzip.php来全权处理了。

<?php
define('ABSPATH', dirname(__FILE__).'/');

$cache = true;//Gzip压缩开关
$cachedir = 'wp-cache/';//存放gz文件的目录,确保可写

$gzip = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
$deflate = strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate');
$encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none');

if(!isset($_SERVER['QUERY_STRING'])) exit();

$key=array_shift(explode('?', $_SERVER['QUERY_STRING']));
$key=str_replace('../','',$key);

$filename=ABSPATH.$key;

$symbol='^';

$rel_path=str_replace(ABSPATH,'',dirname($filename));
$namespace=str_replace('/',$symbol,$rel_path);

$cache_filename=ABSPATH.$cachedir.$namespace.$symbol.basename($filename).'.gz';//生成gz文件路径

$type="Content-type: text/html"; //默认的类型信息

$ext = array_pop(explode('.', $filename));//根据后缀判断文件类型信息
switch ($ext){
  case 'css':
   $type="Content-type: text/css";
   break;
  case 'js':
   $type="Content-type: text/javascript";
   break;
  default:
   exit();
}

if($cache){
if(file_exists($cache_filename)){//假如存在gz文件

  $mtime = filemtime($cache_filename);
  $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';

  if( (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
        array_shift(explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])) ==  $gmt_mtime)
   ){

   // 浏览器cache中的文件修改日期是否一致,将返回304
   header ("HTTP/1.1 304 Not Modified");
   header("Expires: ");
   header("Cache-Control: ");
   header("Pragma: ");
   header($type);
   header("Tips: Cache Not Modified (Gzip)");
   header ('Content-Length: 0');

  }else{

   //读取gz文件输 出
   $content = file_get_contents($cache_filename);
   header("Last-Modified:" . $gmt_mtime);
   header("Expires: ");
   header("Cache-Control: ");
   header("Pragma: ");
   header($type);
   header("Tips: Normal Respond (Gzip)");
   header("Content-Encoding: gzip");
   echo $content;
  }

}else if(file_exists($filename)){ //没有对应的gz文件

  $mtime = mktime();
  $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';

  $content = file_get_contents($filename);//读取文件
  $content = gzencode($content, 9, $gzip ? FORCE_GZIP : FORCE_DEFLATE);//压缩文件内容

  header("Last-Modified:" . $gmt_mtime);
  header("Expires: ");
  header("Cache-Control: ");
  header("Pragma: ");
  header($type);
  header("Tips: Build Gzip File (Gzip)");
  header ("Content-Encoding: " . $encoding);
        header ('Content-Length: ' . strlen($content));
  echo $content;

  if ($fp = fopen($cache_filename, 'w')) {//写入gz文件,供下次使用
                fwrite($fp, $content);
                fclose($fp);
            }

}else{
  header("HTTP/1.0 404 Not Found");
}
}else{ //处理不使用Gzip模式下 的输出。原理基本同上
if(file_exists($filename)){
  $mtime = filemtime($filename);
  $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';

  if( (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
  array_shift(explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])) ==  $gmt_mtime)
  ){

  header ("HTTP/1.1 304 Not Modified");
  header("Expires: ");
  header("Cache-Control: ");
  header("Pragma: ");
  header($type);
  header("Tips: Cache Not Modified");
  header ('Content-Length: 0');

}else{

  header("Last-Modified:" . $gmt_mtime);
  header("Expires: ");
  header("Cache-Control: ");
  header("Pragma: ");
  header($type);
  header("Tips: Normal Respond");
  $content = readfile($filename);
  echo $content;

  }
}else{
  header("HTTP/1.0 404 Not Found");
}
}

?>

  搞定,再去http://www.whatsmyip.org/mod_gzip_test/检 测一下你的网站是否已经开启了GZIP,如果上述步骤都操作正确,那测试结果应该如下图所示。

wange.im

既然已经对CSS和JS文件压缩了,那我们再用YSlow来评测一下等级。Oh耶!我的小站从Grade C上升到Grade B了,不过说实话,虽然把CSS和JS文件压缩了72%,但并没有很明显地提速,因为本来就已经够快的了,哈哈。

说些题外话,根据YSlow的要求,我的小站要从Grade B上升到Grade A怕是有些难度了。因为首页上加载了两个Flash,还有5个JS,拖慢了不少速度,等会有时间我研究下把这5个JS合并,或许会好些吧。我又查了下新 浪、网易、搜狐这样的大型门户网站,也不过Grade B甚至Grade C,我心理平衡一下,嘿嘿。

Continue reading gzip系列

数据仓库(DW)与操作型数据存储(ODS)

1 概述
系统应用集成中一般对各系统中数据分为两类

操作型数据:它 有细节化,分散化的特点

决策型数据:它 有综合化,集成化的特点

数据仓库概念的提出也把数据处理划分为了操作型处 理和分析型处理两种不同类型,从而建立起了DB-DW的两层体系结构。但是有很多情况,DB-DW的两层体系结构并不能涵盖企业所有的数据处理要求,比如 有些实时性决策问题,它要求获取数据周期不能太长,而且也需要一定程度的汇总。这样的问题可以借助于DB-DW的中间层ODS(操作数据存储)来解决。它 象DW一样是一种面向主题,集成的数据环境,又象操作型DB一样包含着全局一致的,细节的当前的数据。我们看下常用的几种系统应用集成需求:

我要了解企业目前的运转情况!(实时监控)

我要知道某地区近5年内的销售情况以制定未来的发展策 略!(决策支持)

我要知道哪些是值得发展的优质的顾客!(预测)

提供企业内部和外部的有用信息以支持中期或远期决策

提供事实的全局信息进行实时监控与临时决策

要满足上面所有的需求,不管是传统的OLTP系统 还是已经集成的数据仓库,都是很难完成任务的。由于这些原因,ODS应运而生。ODS可以看做是围绕主题进行动态整合的一种应用型体系结构,它有如下一些 特点:

从应用子系统获取数据

提供几乎精确到每秒的企业整体应用状态

数据一旦过期就将转入DW

实时决策与预警提示

使用者多为前端业务人员


    2 DW与ODS比 较:

数据仓库ODS
目的决策支持接近实时监控
共同点整合数据整合数据
面向主题面向主题
不同点静态数据
(延迟>24小时)
动态数据
(延迟>1秒)
历史数据当前数据
概括性数据细节化数据




    

实施方案实施结果优势缺陷
数据仓库(DW)企业能够分析DW中的历史数据,进行中远期的规划可以解决企业的决策需求不能满足企业的实时监控和实时业务需求
操作型数据存储(ODS)企业能够把握ODS中的当前综合数据,对企业的及时运行情况随时掌控可以满足企业的实时监控和实时业务需求
不能满足企业的中远期决策需求
DW+ODS(如下图)企业能够分析ODS中的当前综合数据,对企业当前运行情况进行宏观控制;能够分析DW中的历史数据,对未来进行合理规划既能把握实时的企业运作情况,采取及时的应对措施;又能把握历史纵向概况,进行远期战略规划

图1 ODS+DW方案


    4 三种类型的ODS:

类型一:以几秒为间隔的更新(非常贵、不常用、数 据整合能力弱)

类型二:以约1小时为间隔的更新(常用、数据整合 能力较强)

类型三:以约1天为间隔的更新(常用、数据整合能 力较强)

ODS技术的引入和应用,为企业在日常经营中进行 即时OLAP提供了一种解决方案使得企业无须建立一个“臃肿”的DW,就可以进行一些非战略性的的中层决策,来实现对企业的日常管理和控制,同时也能获得 较快的响应速度

Continue reading 数据仓库(DW)与操作型数据存储(ODS)

extjs 分页排序

extjs GridPanel 使用jsonstore的load来加载远程数据:

Ext.onReady(function() {
           var store = new Ext.data.JsonStore({
                       url : 'getRes.ax',//远程加载
                       root : 'rows',//告诉store,server返回json数据rows是本页的记录数组
                       totalProperty : 'count',//告诉store,server返回json数据中count表明总共记录数
                       remoteSort : true,//远程排序
                       fields : [{
                                   name : 'id',
                                   type : 'int'
                               }, {
                                   name : 'actived',
                                   type : 'bool'
                               }, {
                                   name : 'selected',
                                   type : 'bool'
                               }, {
                                   name : 'value',
                                   type : 'string'
                               }],
                       listeners : {
                           beforeload : function() {
                            //beforeloadr事件是添加参数的最好途径
//这里可以添加查询条件   
                               // can add the filter params at here
                               // store.setBaseParam('selected:bool',true);
                               // store.setBaseParam('actived:bool',true);
                               store.setBaseParam('ps', Ext.encode({
                                                   'selected' : true,
                                                   'actived' : true
                                               }));
//结合pagingtoolbar使用时,每次的分页排序,会向serversrvr传参数start[开始的行数],limit[需要的行数],sort[排序的字段],dir[排序方向]。


                           }
                       }
                   });

           var PAGESIZE = 2;

           var grid = new Ext.grid.GridPanel({
                       store : store,
                       bbar : new Ext.PagingToolbar({
                                   store : store,
                                   displayInfo : true,
                                   pageSize : PAGESIZE
                               }),
                       columns : [{
                                   header : 'actived',
                                   sortable : true,
                                   dataIndex : 'actived'
                               }, {
                                   header : 'selected',
                                   sortable : true,
                                   dataIndex : 'selected'
                               }, {
                                   header : 'value',
                                   sortable : true,
                                   dataIndex : 'value'
                               }],
                       columnLines : true,
                       viewConfig : {
                           forceFit : true,
                           emptyText : 'No data available'
                       },
                       stripeRows : true,
                       header : false
                   });

           store.load({
                       params : {
                           start : 0,
                           limit : PAGESIZE
                       }
                   });

           var vi = new Ext.Viewport({
                       layout : 'fit',
                       items : grid
                   });

           vi.show();
       });

Continue reading extjs 分页排序

web安全中的cookie欺骗以及session的安全性

通过这篇文章,可以发现通常所用的session,cookie多么的不安全!但是也不要杯弓蛇影,关键是要依据情景来制定策略!

见互动的文章

session fixation attacks(固定session攻击)

就是要注意,cookie是可以被客户端改动的,也可能客户端开启了第三方cookie,也是可能被窃听的。

那么什么情况下会被入侵:

信任cookie的值:例如用其(固定的值)直接作为登陆凭证,例如被它做了sql注入--安全的做法是:cookie保存随机值,或是加密的用户名密码。你也许会想,加密的用户名密码还不是可以放到修改的cookie里面--是的,但是hackerr怎么得到这个用户名密码?一:它安装木马---这个就不是web程序的原因了,要怪就怪用户自己染上木马。二:用户自己就是hacker,有这个程序的用户名密码--那还费什么劲搞hack,直接输入用户名密码登陆不就得了。(我就是犯了这个牛角尖)

未经加密的http通信中cookie和基于cookie的session都是不安全的,不要用cookie存储机密信息。sessionid这类机密信息应该设置为安全,cookie.setSecure(true);可惜java servlet是没有设置jsession为true的。

不过可以这样:

final HttpSession session = httpRequest.getSession(false);
        if (session != null) {
            final Cookie sessionCookie = new Cookie("JSESSIONID",
session.getId());
            sessionCookie.setMaxAge(-1);
            sessionCookie.setSecure(false);
            sessionCookie.setPath(httpRequest.getContextPath());
            httpResponse.addCookie(sessionCookie);
        } 

话说回来,一个没有使用ssl的站点,加密cookie又有什么用呢?即使监听者获取不了jsessionid之类的cookie,但是他可以看到明文的http内容,他就不需要去猜什么用户名密码之类的。

这样说来还是看安全定位,确实很重要的传输,还是使用ssl吧。

在ssl中还要注意sessionid的安全性:

  1. 一些程序在一些HTTP页面就发送一个令牌,然后再登录页面开始使用https,并且登录时也不修改此令牌,结果最初并未通过验证的用户会话在登录后被升级为通过验证的会话。窃听者可以在登录前就拦截到这个令牌。所以为了提高安全性,程序可以在登录的时候发送令牌或发送一个新令牌。
  2. 程序容许通过HTTP登录,如果攻击者成功将用户的链接降级为HTTP,他仍然能够拦截这个令牌。
  3. 如果所有的页面都是用HTTPS,但图片和一些js,css等的静态文件是使用HTTP传输。这时如果静态文件和登录等页面时在同一个域下,令牌也会通过HTTP泄露。所以将静态文件使用别的域好处是很多的。

CAS的TGC(cookie)还是要配合SSL来保证安全的(这样看来CAS这方面还是有缺陷。),它的TGC一旦被窃听,就会被利用。

总结一下,一般没有加密的http内容都是可以被窃取的,这种情况下,session,cookie,内容等都不是安全的。如果想要提高安全,就是用ssl吧,无论从经济,技术复杂度,架构复杂度来说,都是划算的。但是不是使用了ssl就万事大吉,仍然需要做些防范来保证session令牌的安全性。

 

参见文章:

安全的session管理  http://kazge.com/archives/500.html

session 和 cookie 到底有什麼差別?安全性  http://bbbb7787.blogspot.com/2010/10/session-cookie.html

提高session的安全性(这是对《黑客攻防技术宝典-web实战篇》一书的概括,建议看看这本书) http://hi.baidu.com/billdkj/blog/item/e72ce5257cf93d6b35a80fc2.html

Continue reading web安全中的cookie欺骗以及session的安全性

【转】数据库备份与恢复技术

数据库
数据库就是结构化的数据仓库。人们时刻都在和数据打交道,如:存储在个人掌上电脑(PDA)中的数据、家庭预算电子数据表,企业的财务、仓库数据,银 行、 电信数据等等。对于少量、简单的数据,如果它们与其它数据之间的关联较少或没有关联的情况下,他们可以简单的存放在文件中。当然如果所有的数据结构都很简 单,那么数据库管理系统就没什么用了。但是企业数据都是相关联的。如:职员表链接到名称和地址的记录,订单记录需要与库存信息相对应,海运记录需要与信用 额度相对应,等等。通常来说,不可能使用普通的记录文件来管理大量的、复杂的系列数据,如:银行的客户数据,或者生产厂商的的生产控制数据。普通记录文件 没有系统结构来系统的反映数据间的复杂关系,它也不能强制定义个别数据对象。
数据库管理系统
     数据库管理系统(DBMSs),或者数据库管理器,已经发展了近二十年,来解决上面提到的这些需求。数据库管理器是近似于文件系统的软件系统,通过它, 应 用程序和用户可以取得所需的数据。然而,它们又不象文件系统,它们定义了所管理的数据之间的结构和约束关系。并且,数据库管理器提供了一些基本的数据管理 功能:
 数据安全: 在商业上,数据库必须是一个可以存储数据的安全的地方。数据库管理器必须提供有效的备份和恢复能力,来确保在灾难和错误后,数据能够尽快的可以被应用所访 问。
 数据权限: 对于一个企业来说,它把关键的和重要的数据存放在数据库中,数据库管理系统必须能够防止未授权的数据访问。
数据共享: 一个数据库必须允许多个应用和用户同时进行数据访问,而且不影响数据的完整性。例如:如果两个用户试图同时修改同一条记录,两个修改操作都必须被处理,并 且产生一个可理解的干净的结果。
 数据 组织: 基于文件的数据的主要优势就在于它利用了数据结构。数据库的结构使开发者避免了针对每一个应用都需要重新定义数据逻辑关系的过程。
数据库数据模型
数据库管理系统的发展已经经历了一个漫长复杂的过程。人们提出了许多数据模型,并一一实现。其中比较重要的三个就是:
 分 级模型: 在一个分级的数据库中,数据项间具有父项与子项的关系。例如:一个顾客的记录包括名称和地址信息,它可能就是一系列订单记录的父项,每个订单记录包含了关 于这个订单的详细信息。
网络 模型: 在一个网络数据库中,数据项之间有更多的相互关系。这些关系通常用来描述一个图形形状,或者网络中形成刀片结构的那些节点和它们之间的关系。
关 系模型: 在关系型数据库中,数据项保存在行中,文件就象是一个表。关系被描述成不同数据表间的匹配关系。一个区别关系模型和网络及分级型数据库的重要一点就是数据 项关系可以被动态的描述或定义,而不需要因为结构被改变而卸载然后重新加载数据库。
关系模型
早在1980年,数据库市场就被关系型数据库管理系统所占领。关系模型的成功并不在意料之外。这个模型基于一个可靠的基础,它可以简单并恰当的将数据 项描 述成为表(table)中的记录行(raw)。关系模型第一次广泛的推行是在1980年中,是因为一种标准的数据库访问程序语言被开发出来,它被称作结构 查询语言(SQL)。
今天,成千上万使用关系型数据库的应用程序已经被开发出来,包括跟踪客户端处理的银行系统,仓库货物管理系统,客户关系管理(CRM)系统,以及人力 资源 管理系统。由于数据库保证了数据的完整性,企业通常将他们的关键业务数据存放在数据库中。因此保护数据库避免错误以及灾难已经成为企业所关注的重点。
数据库备份中的一致性和实时性
一致性和实时性
  一个一致性的数据库就是指数据处理响应完成了的数据库。例如:一个会计数据库,当它的记入借方与相应的贷方记录相匹配的情况下,它就是数据一致的。 一个 实时的数据库就是指所有的事务全部执行完毕后才响应。如果一个正在运行数据库管理的系统崩溃了,而对事务的处理结果还存在缓存中而没有写入到磁盘文件中的 情况,当系统重新启动时,系统数据就是非实时性的。数据库日志被用来在灾难发生后恢复数据库时保证数据库的一致性和实时性。
数据库恢复
     正规的数据库备份是最基本和有效的数据库容灾技术。数据库备份和恢复技术与我们在错误!未找到引用源。中讲述的文件系统的备份和恢复是不同的。
数 据库事务
如果要明白备份恢复技术,明白数据库事务的种类是很有用的。一个事务就是一个事务活动所引起的一系列的数据库操作。例如,一个会计事务可能是由以下部 分组 成:
读取借方数据
减去借方记录中的借款数量
重写借方记录
读取贷方记录
在贷方记录上的数量加上从借方扣除的数量
重写贷方记录
写一条单独的记录来描述这次操作,以便日后审计所有这些操作组成了一个事务,描述了一个业务动作。在上述例子中,无论借方的动作或是贷方的动作哪一个 没有 被执行,数据库都不会反映该业务执行正确。
数据库管理系统在数据库操作时强迫进行事务定义,这意味着或者一个事务定义的应用的全部操作结果都反映在数据库中,或者都没有反映在数据库中,即使数 据库 在事务执行过程中崩溃的情况下。
事务定义是关系数据库中最重要的关系之一。上述例子包含了两个数据库操作:从借方数据中扣除资金,并且在贷方记录中加入这部分资金。如果系统在执行事 务的 过程中崩溃,如果此时已修改完毕借方数据,但还没有修改贷方数据,资金就会在此时物化。把这两个步骤合并成一个事务命令,这样在数据库系统执行时,要么全 部完成,要么全部不完成,但当只完成一步时,系统是不会对已作的这一步做出响应的。
数据库崩溃恢复
一个运行着数据库系统的计算机随时都可能宕机。然而“已借未贷”或“已贷未借”的情况都可能出现。当系统崩溃后重启时,数据库管理系统必须允许这种可 能性 的发生,也就是说,在磁盘数据文件中可能包含一些部分完成的事务,在应用能够访问数据库数据之前,这些必须全部被检出。
防止上述情况发生的基本技术就是保存一份连续日志,记录将做的和完成的操作。当需要修复损坏的数据库时,数据库系统重新应用这些日志,寻找那些将要执 行但 未完成的任务。如果任何类似的事务的已经在数据库中反映,这一定是颠倒的,并且数据库必须回滚。
使用这种日志重新应用的技术,数据库系统可以避免宕机所带来的已接受事务(应用已确认执行完毕的事务)的丢失。数据修复时,那些在宕机时处理结果还存 在缓 存中的已接受事务,结果会存放到磁盘文件中。未接受事务(还没有被应用确认的事务)会被回滚,消除它所带来的对其他数据的影响。
文件系统缓存和数据库恢复
  如果使用文件作为数据库的数据存储方式会给我们的讨论增加一些额外的复杂性,因为文件管理系统有它自己的缓存。如果一个数据库系统在宕机后立 刻重新启动,那么它所包含的文件可能并没有实时刷新到存储中,这是由于在系统宕机时,文件系统的缓存没有写入到存储的原因 。在这种情况下,数据库恢复进程必须重新应用数据日志刷新。也就是说,在系统宕机的情况下已接受事务也不会丢失。
归档日志:
  长时间的数据库恢复
    大多数的数据库都支持数据库日志归档,支持企业保存一个长时间的数据刷新历史。如果所有的基于一次全备份时间点之后的归档数据全部可用,那么就可以通过以 下步骤恢复成为一个最新的数据库。
  恢复备份拷贝
  在已经恢复的数据库上重新应用所有的归档日志。
  在已经恢复的数据库上重新应用数据库日志。
  这也许是一个灾难后完全丧失服务能力数据中心的唯一恢复数据方法。在这个工作过程步骤中,数据库全备份数据和归档日志必须快速的传送到灾难恢复中心。为了 实现完全的实时数据库恢复,镜像或复制当前的数据库日志到恢复中心也是必需的。
数据库备份技术
  就如前面对恢复作用的描述,一个数据库的数据库备份必须必须是一个数据库的完整的映像,在这个映像的时间点上,没有部分完成的事务存在。这可以通过数据库 的离线备份来实现,因为在这种情况下,没有事务需要处理。这种方式的缺点在于,在备份过程中,没有应用能够使用数据库。数据库在线的时候也可以进行备份, 在这种情况下,备份程序要确保不管数据访问多么活跃,都能够得到一个完整的数据拷贝。
离线数据备份
  如果备份时数据库不可以被应用所访问,那么我们称这种备份为离线备份或冷备份。冷备份可以通过关闭数据库然后进行文件备份来实现。离线数据库备份是简单 的,也是被认为有效的备份技术。但是逐渐的,企业发现把他们的数据库停下来,然后进行备份,这种方式完全不切实际。而且,在一些老的数据库管理系统中,冷 备份拷贝不能用来进行前滚,因为它们与数据库日志不同步。在新的数据库设计中,已经解决了冷备份拷贝与数据库日之间的同步问题,所以前面的问题也就逐渐不 成为问题了。
在线数据库备份
  现在大多数的数据库都可以在应用进行数据访问时进行数据备份。在备份活跃数据库时有两种基本技术,被称作逻辑的和物理的在线分别备份。
  大多数数据库管理系统都支持逻辑在线备份。例如:被包含在Oracle数据库的RMAN工具和Sybase数据库的“dump database”命令。逻辑在线备份之所以这么命名,是因为它拷贝了数据库的逻辑单元,而不是存储设备列表或是存储逻辑单元的文件。逻辑数据库备份工具 通常和恢复、修复工具放在一起,因此产生有问题备份的几率较小。逻辑数据库备份的主要缺点就是他无法利用存储设备的快照技术来减少对应用的影响。因为在一 个逻辑数据库备份的过程中,系统性能会大大的降低,因此它对总是处在活跃状态的数据库并不合适。
在线数据库备份也可以通过物理的备份数据库底层所 包含的文件来实现。数据库的数据文件并不是随时都可以进行拷贝的,因为数据库始终在不断的刷新这些文件。一个文件的拷贝包含有非全部完整事务的概率很高, 而且也不要期望通过数据库修复来恢复数据的一致性。
  要确保一个具有一致性的系列文件备份,数据库必须处于一个静止状态,没有事务提交,也没有缓存的数据需要写到存储中。当备份结束后,数据库可以被重新激 活。当然在数据库备份时,数据库时不可用的,这样的结果与离线数据备份基本相同。
  一些文件系统和卷管理器支持数据快照。如果可以制作一个基于数据库所包含文件的快照,那么数据库只需要在快照初始化的一瞬间是静止的即可。一旦快照初始化 完毕,数据库就可以重新提供访问能力。快照使备份可以进行在应用正在访问“实际”的数据库的过程中。因此,物理数据库备份可以是在(近)线的,而且能够保 证数据库拷贝的一致性。
数据库静止状态
  不同的数据库支持不同的备份技术。例如:Oracle,将表空间(一组表)置于在线的备份模式。这样的一份备份数据就可能不一致,因为数据库会在备份模式 下会不断的刷新数据。然而恢复却可以是一致的,因为在备份过程中,额外的信息被数据库的管理日志记录下来。重新应用日志记录了数据库在备份状态下的变化, 并将它恢复成一致的状态。
  其它的数据库管理,包括Sybase ASE和DB2,会暂时的挂起所有事务并且将缓存保存,这样一个基于快照的数据库所包含文件的拷贝就是一个一致性的数据库拷贝。
  包含了卷管理和文件系统快照的物理数据库备份技术是一种非常强大的技术,因为它基本消除了应用不能进行数据访问的备份窗口时间。例如:数据库可以将数据直 接或间接的存储在镜像的逻辑卷上。要进行备份的情况下,数据库管理员可以将数据库暂时静止,将镜像卷与存储分离,然后重新激活数据库。被分离的镜像卷包含 了一个一致的数据拷贝,可以通过它完成一个数据库复制。这种情况下的应用不能访问数据库的备份窗口只有几秒钟或几分钟。
  当被剥离的镜像卷连接到存储网络中,可使用一个额外的服务器来进行数据的拷贝。因为使用了独立的存储设备和服务器资源,因此对数据库应用性能没有任何影 响。唯一的缺点是,当基于被剥离的镜像卷的数据库备份拷贝完成后,镜像卷需要重新连接到数据库存储卷中。重新同步所有的I/O会影响应用的I/O。镜像技 术可以记录所有在剥离镜像卷后改变了的数据,在镜像卷重新接入后快速同步数据,这种技术使同步对应用的影响降到最低。图1-4使用流程图描述了脱离主机的 备份方式。流程图同样强调了快照可以在运行着数据库的服务器上进行备份。
数据库增量备份
  数据库的不断增长和对可用性要求的提高,使数据库全备份在许多情况下无法完成。如同文件系统一样,如果在两次备份间只有少量的数据变化,数据库增量备份可 以缩短数据库备份时间。如果只是变化了的数据被拷贝,可以节省备份时间和备份介质。与全备份类似,增量备份也可以是逻辑的或是物理的。
逻辑增量备份
  归档数据库日志是逻辑数据库增量备份的一种方式。通过恢复一次数据库全备份和重新应用归档日志,可以将数据库恢复到归档的最新时刻。把全备份数据和所有的 归档日志存放在一个安全的地方,是一个很有用处的恢复技术。
  随着归档日志的堆积,恢复时间和对介质的占用都会随之增长。对于每一个企业,都有一个对增量恢复窗口的可容忍的极限。因此,增量备份策略应该包含定期的数 据库全备份,以便经常建立新的基点。
  一些数据库管理器可以在数据库正在运行时执行数据库逻辑增量备份。一个逻辑增量备份在开始时首先检测自上次备份后改变了的数据块的列表。这些块被读取并被 传送到备份服务器。增量备份减少了全备份必须被执行的频率。使用这种技术,数据库恢复就可以是自动的,因为数据库管理器的恢复功能可以从以前的全备份和后 来的增量备份创建一个较新的数据库映像。增量备份使数据库性能只是稍有加强,因为数据库管理器必须创建一个变化数据块的列表。
物理增量备份
  包含数据库系列文件的文件系统的增量备份有效的创建了一个数据库的物理增量备份。但当一个数据库管理器刷新表中的一行数据时,只有包含这条数据的文件块改 变了,其余的文件块并没有受到影响。然而针对文件的任何改变都会导致在增量备份中整个文件被拷贝,这种基于文件的增量备份通常等同于数据库全备份。如果数 据库管理器或者备份程序能够识别在数据库文件中变化了的数据块,就可以只备份变化的数据块;有这么一种技术被称作数据块级增量备份。
  在使用快速镜像不太现实的情况下,除使用更少的备份介质外,数据库增量备份能够减小备份窗口。一些数据库管理器能够执行透明的数据库物理增量备份和恢复, 使对个别增量备份的管理降到最低。要完成一个基于块级别的备份的数据库恢复,首先应恢复最新的全备份,然后重新应用所有的后面的增量备份来恢复数据库映 像。
  恢复任何的增量备份都需要花费时间。数据库管理员通常制定周期全备份计划,以便定期改变新的数据恢复基点,限制最高的增量跳越数量(和最糟糕情况下的恢复 时间)。一些备份程序可以实现被称为“合成全备份”的功能,这是通过第二台服务器上的增量来实现的。这些合成全备份在每一次增量的恢复后都会产生一个新的 基点。
从备份恢复数据库
   通过正规备份,并且快速的将备份介质运送到安全的地方,数据库就能够在大多数的灾难中得到恢复。恢复是文件的使用是从一个基点的数据库映像开始,到一些 综合的备份和日志。由于不可预知的物理灾难,一个完全的数据库恢复(重应用日志)可以使数据库映像恢复到尽可能接近灾难发生的时间点的状态。对于逻辑灾 难,如:人为破坏或者应用故障等,数据库映像应该恢复到错误发生前的那一点。
  在一个数据库的完全恢复过程中,基点后所有日志中的事务被重新应用,所以结果就是一个数据库映像反映所有在灾难前已接受的事务,而没有被接受的事务则不被 反映。为了恢复数据库误操作等错误,完全的恢复时不合适的,因为如果重新应用所有事务,错误就会重复。数据库恢复应用程序允许管理员停止日志前滚在错误发 生前一点。数据库恢复可以恢复到错误发生前的最后一个时刻。

检验数据库备份
大多数的企业都会定期地对他们的数据库进行备份,但是却没能经常地对数据库备份进行检验。数据库备份会由于以下各种原因而变得无效:
  元数据(例如一个Oracle控制文件或SQL Server控制数据库)缺失
  在物理备份的过程中数据库处于非静止的状态
  一个或多个必需的数据库文件从备份中丢失
  数据库被破坏后才进行备份
  从某种意义而言,无效的数据库备份比根本不做备份的情况更糟,因为无效的数据库备份会造成一种安全的假象。因此,检验数据库备份是一项非常重要的工作,尤 其是在进行了自动备份进程之后或是在数据库结构改变之后。即使是没有任何改变发生,定期进行检验也是必需的,例如可以定期地检测损坏的介质或磁带驱动器等 等。
  为了将检验工作对数据库生产运行的影响冲突降至最低,备份的检验工作需要使用备用资源。一个正在用于检验修复备份的数据库应该带有一个备用数据库标识符或 服务器名称以免客户端会将信息错误地发送给检验数据库。检验内容应该既包括使用全部的归档日志的数据库恢复资源,也包括使用厂商提供工具进行数据库一致性 的检验。
管理数据库日志
  对于容灾而言,数据库备份应当存贮在远离数据库的地方。为了达到最优容灾状态,在灾难发生后能够容易地获取数据库日志也是非常必要的。数据库归档日志通常 保存在备份储存的地点。数据库管理员必须在数据库实时恢复和资源占用量两者之间找到平衡,从而决定进行数据库日志归档的频率。过多地进行归档可以降低数据 损失的潜在危险,但是浪费了更多的进程和I/O资源,很有可能增加了处理的响应时间。过少地进行归档可以降低资源的平均占用量,但是延长了两次归档的间隔 时间,很有可能导致不能做到精确的实时恢复。
  如果一个数据库和它的联机日志被损坏了,那么即使马上进行了严密的数据库备份和日志归档,数据也极有可能丢失。因此,一个完整的数据库融灾策略的一个重要 部分就是对联机的数据库日志进行复制,这样在进行修复处理时就可以及时利用这些复制的内容准确无误地修复数据库。联机数据库日志可以通过有限的距离进行镜 像。如果距离过长,数据库管理员可以通过多路转接技术或者通过企业网络同时进行本地和远处的日志拷贝。多路转接技术通常比镜像和低水平复制(如数据卷)的 速度要慢一些,因此如果可以的话要尽量选择后一种方式。
  最高级别的数据库实时恢复是在每次事务提交的之前同步进行数据库日志的传输和归档。换句话说,必须要在日志已经被转移到另外地点后,才进行事务的提交。显 而易见,这种选择执行起来的代价是非常昂贵的,因而在实践中较少采用。
数据库复制
  周围环境的灾难,例如:地震、洪水,或者暴乱会使整个数据中心及周围环境彻底丧失数据服务的能力。为了恢复一个灾难中的数据库,必须有一个灾难破坏范围之 外的一个数据的冗余拷贝,并且当恢复中心数据恢复完毕后,必须可以响应客户端的请求。
使用传输日志方法的复制
  保证数据库可恢复的环境容灾的最基本挑战就是如何保证容灾地点具有最新的数据。或许实现这种状态的最简单办法就是在数据中心与容灾地点间物理的传送归档日 志,例如,使用快递或运输服务。在恢复地点,日志被备份数据库(有的叫做备用数据库)重新应用来实现数据“刷新”。对于应用来说,如果可以容忍一整天的数 据丢失,那么这种简单的方法也许就足够了。它的主要缺点就是数据刷新和重路由客户端请求的劳动强度比较高,很容易出现人为错误。
  对于简单应用和小型数据库,日志传输和数据库刷新技术可以通过在网络上执行定期的日志传送和刷新任务来自动完成。
数据库管理器复制
  今天,大多数数据库管理器支持精密的实施复制,允许例如一对多、双向的复制。也可以是数据库子集的复制,只将感兴趣的数据复制到目标端。在分布式数据库最 初设计时,数据库管理器复制就可以用来实现容灾复制。
  数据库复制需要在主站点和恢复站点间有一个高性能网络的连接,但即使这样也不能实时同步,这意味着在数据库完全将数据反映到备用数据库有一个时间延迟。一 些数据库支持同步复制,虽然解决了上面的问题,但是它明显减低了主数据库的性能,因为只有当所有的复制任务全部结束后,数据库才能继续接收数据。
针对数据库的存储复制
  数据库复制要求具有专门的数据库管理技术。额外的,一些基于数据库的信息服务在数据库之外存储了额外的数据。为了实现灾难恢复,所有的应用信息都必须复制 到远程站点。存储复制是针对数据库复制的一个简单可行的办法,它可以将数据库数据和其它数据全部从数据中心复制到恢复站点。
  数据库的内容可以在卷一级或者文件一级进行复制。稳定的、高性能的文件系统复制也相当优秀。可靠的高性能卷复制能够使远程复制数据文件、在线日志和归档日 志变得非常简单。如果所有的数据文件、日志文件,及其它的辅助数据都存放在一个独立的文件系统或独立的卷中,复制就会变得非常简单。
  卷管理器对数据库恢复的非常有利,原因在于它在数据中心与容灾站点执行同样顺序的操作。如果没有这个特点,它的数据不能够保证与实际数据库先前的状态一 致,也就是说复制就不能作为数据库恢复的基础。
复制的延时
无论数据库复制还是存储复制,都会将认为无操作和应用错误复制到恢复端。例如,在主站点错误的删除了一个表,那么恢复中心也同会删除,因此使用复制无法纠 正错误。
    如果在数据中心与恢复中心的数据刷新上存在一定的可配制的延时,数据库复制就可以用来一些逻辑错误。例如,如果复制日志在应用到备用数据库前保留一小时, 而逻辑错误在一小时之内被发现(通常是这样),这样错误还没有反映到复制数据库中。可以立即将复制停止,然后可以使用备用数据库恢复主数据库。
全局群集管理
  因为更加坚固,在群集上,数据库应用可以同时运行在主站点和备用站点。在一个单独的数据中心内两个或更多的群集计算机可以通过一个大范围的网络连接成全局 群集,他们是一个平等的高可用系统。当配制了多个恢复站点,或者一个恢复站点复制到第二个恢复站点,可以使用全局群集进行复杂的管理。
  在错误切换的复杂性上和站点切换操作的限制性上,全局群集不同于本地群集。与本地群集相比,全局群集的错误切换往往不是自动的,管理员的决定通常需要经过 严肃的考虑。全局群集的任务就是方便的提供一个分布的高可用服务器的全局的视图,并且可以通过一个单独的点来进行控制。
概要:数据库恢复层次
  下面列出的数据库恢复技术是按照他们所能够提供的保护能力的顺序列出的,也同时是使用他们所需要的资金,方便程度,和技术复杂性的排列顺序。每一种技术都 必须与他前面的技术共同使用。例如,磁盘镜像必须伴随着数据库备份和日志归档。
  正规数据库备份和日志归档
  磁盘镜像
  本地群集
  数据库复制
全局群集
  对于希望恢复时间(RTO)时间在几个小的内的企业,正规的数据库备份和日志归档也许就能满足。数据库备份和归档日志应该被保存在离数据中心有一定距离的 地方。高级的备份软件的特性,如自动的定期的块增量备份可以减少管理成本,缩短备份窗口,以及最小化恢复时间。
  通过简单的镜像硬件和网络,镜像数据库存储dada减少了因为硬件故障所引起的数据库停机。也可以通过剥离镜像的备份提高数据库的可用性。
  可以通过群集技术提高数据库级信息服务的可用性。一个本地群集可以使由于系统的单点而出现故障的可能降到最小。当错误引起临时的损耗,服务恢复时自动的。 在共享数据的群集中,损耗窗口可以为零。在群集中,备份可以运行在导入了数据库服务器镜像数据的辅助服务器上。
  为防止站点实效而进行的灾难恢复中,数据库必须复制到远程站点。数据库复制的最简单方法就是将归档日志传送到远端,然后在备用服务器上重新应用。这种技术 丢失的数据数量是固定的。不能容忍在灾难中丢失数据的企业应该使用数据库或存储复制。
  最高的数据库可用性应该使用全局群集来完成,它在多个互相连接的站点中调整数据库和应用的可用程度。
  弹性数据库通过不同方法使用冗余拷贝。数据库可以通过额外的拷贝提高访问性能。存储数据库数据的磁盘可以被镜像来提高弹性。实时备份可以用来避免灾难和故 障。事务日志可以使导致数据错误的时间前滚。最后,完全的数据镜像可以在远端保存一份数据来避免灾难。高级的数据库管理器可以自行分发它们所管理的数据以 提高数据库弹性和性能。

Continue reading 【转】数据库备份与恢复技术

HtmlEditor key 事件

ux.HtmlEditor = Ext.extend(Ext.form.HtmlEditor, {

          frame : true,

          initComponent : function() {

              ux.HtmlEditor.superclass.initComponent.call(this);

              this.addEvents('submit');

          },

          initEditor : function() {

             ux.HtmlEditor.superclass.initEditor.call(this);

              if (Ext.isGecko) {

                  Ext.EventManager.on(this.doc, 'keypress', this.fireSubmit,

                          this);

              }

              if (Ext.isIE || Ext.isWebKit || Ext.isOpera) {

                  Ext.EventManager.on(this.doc, 'keydown', this.fireSubmit,

                          this);

              }

          },

          fireSubmit : function(e) {

              if (e.ctrlKey && Ext.EventObject.ENTER == e.getKey()) {

                  this.fireEvent('submit', this);

              }

          }

      });

Continue reading HtmlEditor key 事件

Pagination


Total views.

© 2013 - 2019. All rights reserved.

Powered by Hydejack v6.6.1