<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,  comments - 11,  trackbacks - 0
     

    1.the die is cast?? 一切已成定局

    posted @ 2007-12-13 12:37 jadmin 閱讀(56) | 評論 (0)編輯 收藏
  • ! (logical NOT)
  • != (not equal)
  • % (modulo)
  • % (wildcard character)
  • & (bitwise AND)
  • && (logical AND)
  • () (06-3)
  • (Control-Z) \z
  • * (multiplication)
  • + (addition)
  • - (subtraction)
  • - (unary minus)
  • / (division)
  • /etc/passwd
  • < (less than)
  • << (left shift)
  • <= (less than or equal)
  • <=> (Equal to)
  • <> (not equal)
  • = (equal)
  • > (greater than)
  • >= (greater than or equal)
  • >> (right shift)
  • \0 (ASCII 0)
  • \\ (escape)
  • \b (backspace)
  • \n (newline)
  • \r (carriage return)
  • \t (tab)
  • \z (Control-Z) ASCII(26)
  • ^ (bitwise XOR)
  • _ (wildcard character)
  • `
  • A

  • ABS()
  • ACOS()
  • ADDDATE()
  • addition (+)
  • AES_DECRYPT()
  • AES_ENCRYPT()
  • ALTER COLUMN
  • ALTER TABLE
  • AND, bitwise
  • AND, logical
  • AS
  • AS
  • ASCII()
  • ASIN()
  • ATAN()
  • ATAN2()
  • AVG()
  • B

  • backspace (\b)
  • BEGIN
  • BENCHMARK()
  • BETWEEN ... AND
  • BIGINT
  • BIN()
  • BINARY
  • BIT
  • BIT_AND()
  • BIT_COUNT()
  • BIT_LENGTH()
  • BIT_OR()
  • BLOB
  • BLOB
  • BOOL
  • C

  • CASE
  • CAST
  • CEILING()
  • CHAR
  • CHAR
  • CHAR VARYING
  • CHAR()
  • CHAR_LENGTH()
  • CHARACTER
  • CHARACTER VARYING
  • CHARACTER_LENGTH()
  • COALESCE()
  • Comment syntax
  • COMMIT
  • CONCAT()
  • CONCAT_WS()
  • CONNECTION_ID()
  • control flow functions
  • CONV()
  • CONVERT
  • COS()
  • COT()
  • COUNT()
  • COUNT(DISTINCT)
  • CREATE DATABASE
  • CREATE INDEX
  • CREATE TABLE
  • CROSS JOIN
  • CURDATE()
  • CURRENT_DATE
  • CURRENT_TIME
  • CURRENT_TIMESTAMP
  • CURRENT_USER()
  • CURTIME()
  • D

  • DATABASE()
  • DATE
  • DATE
  • DATE_ADD()
  • DATE_FORMAT()
  • DATE_SUB()
  • DATETIME
  • DATETIME
  • DAYNAME()
  • DAYOFMONTH()
  • DAYOFWEEK()
  • DAYOFYEAR()
  • DEC
  • DECIMAL
  • DECODE()
  • DEGREES()
  • DELAYED
  • DELETE
  • DES_DECRYPT()
  • DES_ENCRYPT()
  • DESC
  • DESCRIBE
  • DISTINCT
  • DIV
  • division (/)
  • DO
  • DOUBLE
  • DOUBLE PRECISION
  • double quote (\")
  • DROP DATABASE
  • DROP INDEX
  • DROP INDEX
  • DROP PRIMARY KEY
  • DROP TABLE
  • DUMPFILE
  • E

  • ELT()
  • ENCODE()
  • ENCRYPT()
  • ENUM
  • ENUM
  • equal (=)
  • escape (\\)
  • EXP()
  • EXPORT_SET()
  • EXTRACT()
  • F

  • FIELD()
  • FILE
  • FIND_IN_SET()
  • FLOAT
  • FLOAT
  • FLOAT(M,D)
  • FLOAT(precision)
  • FLOAT(precision)
  • FLOOR()
  • FORCE INDEX
  • FORCE INDEX
  • FORMAT()
  • FOUND_ROWS()
  • FROM_DAYS()
  • FROM_UNIXTIME()
  • G

  • GET_LOCK()
  • greater than (>)
  • greater than or equal (>=)
  • GREATEST()
  • GROUP_CONCAT()
  • H

  • HANDLER
  • HEX()
  • hexadecimal values
  • HOUR()
  • I

  • identifiers, quoting
  • IF()
  • IFNULL()
  • IGNORE INDEX
  • IGNORE INDEX
  • IGNORE KEY
  • IGNORE KEY
  • IN
  • INET_ATON()
  • INET_NTOA()
  • INNER JOIN
  • INSERT
  • INSERT ... SELECT
  • INSERT DELAYED
  • INSERT()
  • INSTR()
  • INT
  • INTEGER
  • INTERVAL()
  • IS NOT NULL
  • IS NULL
  • IS_FREE_LOCK()
  • ISNULL()
  • ISOLATION LEVEL
  • J

  • JOIN
  • K

    L

  • LAST_INSERT_ID([expr])
  • LCASE()
  • LEAST()
  • LEFT JOIN
  • LEFT OUTER JOIN
  • LEFT()
  • LENGTH()
  • less than (<)
  • less than or equal (<=)
  • LIKE
  • LIMIT
  • LN()
  • LOAD DATA INFILE
  • LOAD_FILE()
  • LOCATE()
  • LOCATE()
  • LOCK TABLES
  • LOG()
  • LOG10()
  • LOG2()
  • LONGBLOB
  • LONGTEXT
  • LOWER()
  • LPAD()
  • LTRIM()
  • M

  • MAKE_SET()
  • MASTER_POS_WAIT()
  • MATCH ... AGAINST()
  • MAX()
  • MD5()
  • MEDIUMBLOB
  • MEDIUMINT
  • MEDIUMTEXT
  • MID()
  • MIN()
  • minus, unary (-)
  • MINUTE()
  • MOD()
  • modulo (%)
  • MONTH()
  • MONTHNAME()
  • multiplication (*)
  • mysql_info()
  • mysql_info()
  • mysql_info()
  • mysql_info()
  • mysql_real_escape_string()
  • N

  • NATIONAL CHAR
  • NATURAL LEFT JOIN
  • NATURAL LEFT OUTER JOIN
  • NATURAL RIGHT JOIN
  • NATURAL RIGHT OUTER JOIN
  • NCHAR
  • newline (\n)
  • NOT BETWEEN
  • not equal (!=)
  • not equal (<>)
  • NOT IN
  • NOT LIKE
  • NOT REGEXP
  • NOT, logical
  • NOW()
  • NUL
  • NULL value
  • NULLIF()
  • NUMERIC
  • O

  • OCT()
  • OCTET_LENGTH()
  • OLD_PASSWORD()
  • OR, bitwise
  • OR, logical
  • ORD()
  • ORDER BY
  • P

  • parentheses ( and )
  • PASSWORD()
  • PERIOD_ADD()
  • PERIOD_DIFF()
  • PI()
  • POSITION()
  • POW()
  • POWER()
  • PRIMARY KEY
  • PRIMARY KEY
  • Q

  • QUARTER()
  • QUOTE()
  • quoting of identifiers
  • R

  • RADIANS()
  • RAND()
  • REAL
  • REGEXP
  • RELEASE_LOCK()
  • RENAME TABLE
  • REPEAT()
  • REPLACE
  • REPLACE ... SELECT
  • REPLACE()
  • return (\r)
  • REVERSE()
  • RIGHT JOIN
  • RIGHT OUTER JOIN
  • RIGHT()
  • RLIKE
  • ROLLBACK
  • ROUND()
  • RPAD()
  • RTRIM()
  • S

  • SEC_TO_TIME()
  • SECOND()
  • SELECT
  • SESSION_USER()
  • SET
  • SET
  • SET TRANSACTION
  • SHA()
  • SHA1()
  • SIGN()
  • SIN()
  • single quote (\')
  • SMALLINT
  • SOUNDEX()
  • SOUNDS LIKE
  • SPACE()
  • SQL_CACHE
  • SQL_NO_CACHE
  • SQRT()
  • START TRANSACTION
  • STD()
  • STDDEV()
  • STRAIGHT_JOIN
  • STRCMP()
  • SUBDATE()
  • SUBSTRING()
  • SUBSTRING()
  • SUBSTRING_INDEX()
  • subtraction (-)
  • SUM()
  • SYSDATE()
  • SYSTEM_USER()
  • T

  • tab (\t)
  • TAN()
  • TEXT
  • TEXT
  • TIME
  • TIME
  • TIME_FORMAT()
  • TIME_TO_SEC()
  • TIMESTAMP
  • TIMESTAMP
  • TINYBLOB
  • TINYINT
  • TINYTEXT
  • TO_DAYS()
  • TRIM()
  • TRUNCATE
  • TRUNCATE()
  • Types
  • U

  • UCASE()
  • unary minus (-)
  • UNION
  • UNIQUE
  • UNIX_TIMESTAMP()
  • UNLOCK TABLES
  • UPDATE
  • UPPER()
  • USE
  • USE INDEX
  • USE INDEX
  • USE KEY
  • USE KEY
  • USER()

    V

  • VARCHAR
  • VARCHAR
  • VARIANCE()
  • VERSION()
  • W

  • WEEK()
  • WEEKDAY()
  • Wildcard character (%)
  • Wildcard character (_)
  • X

  • XOR, bitwise
  • XOR, logical
  • Y

  • YEAR
  • YEAR
  • YEAR()
  • | (bitwise OR)
  • || (logical OR)
  • ~
  • posted @ 2007-12-03 07:26 jadmin 閱讀(89) | 評論 (0)編輯 收藏

      每次我想要演示實際代碼時,我會對mysql客戶端的屏幕就出現的代碼進行調整,將字體改成Courier,使他們看起來與普通文本不一樣(讓大家區別程序代碼和正文)。在這里舉個例子:


    mysql> DROP FUNCTION f;
    Query OK, 0 rows affected (0.00 sec)

      如果實例比較大,則需要在某些行和段落間加注釋,同時我會用將"<--"符號放在頁面的右邊以表示強調。例如:


    mysql> CREATE PROCEDURE p ()
    -> BEGIN
    -> /* This procedure does nothing */ <--
    -> END;//
    Query OK, 0 rows affected (0.00 sec)

      有時候我會將例子中的"mysql>"和"->"這些系統顯示去掉,你可以直接將代碼復制到mysql客戶端程序中(如果你現在所讀的不是電子版的,可以在mysql.com網站下載相關腳本)

      所以的例子都已經在Suse 9.2 Linux、Mysql 5.0.3公共版上測試通過。在您閱讀本書的時候,Mysql已經有更高的版本,同時能支持更多OS了,包括Windows,Sparc,HP-UX。因此這里的例子將能正常的運行在您的電腦上。但如果運行仍然出現故障,可以咨詢你認識的資深Mysql用戶,這樣就能得到比較好的支持和幫助。

    Why Triggers 為什么要用觸發器

      我們在MySQL 5.0中包含對觸發器的支持是由于以下原因:

      MySQL早期版本的用戶長期有需要觸發器的要求。
      我們曾經許諾支持所有ANSI標準的特性。
      您可以使用它來檢查或預防壞的數據進入數據庫。
      您可以改變或者取消INSERT, UPDATE以及DELETE語句。
      您可以在一個會話中監視數據改變的動作。

      在這里我假定大家都讀過"MySQL新特性"叢書的第一集--"MySQL存儲過程",那么大家都應該知道MySQL至此存儲過程和函數,那是很重要的知識,因為在觸發器中你可以使用在函數中使用的語句。特別舉個例子:

      復合語句(BEGIN / END)是合法的.

      流控制(Flow-of-control)語句(IF, CASE, WHILE, LOOP, WHILE, REPEAT, LEAVE,ITERATE)也是合法的.

      變量聲明(DECLARE)以及指派(SET)是合法的.

      允許條件聲明.

      異常處理聲明也是允許的.

      但是在這里要記住函數有受限條件:不能在函數中訪問表.因此在函數中使用以下語句是非法的。


    ALTER 'CACHE INDEX' CALL COMMIT CREATE DELETE

    DROP 'FLUSH PRIVILEGES' GRANT INSERT KILL

    LOCK OPTIMIZE REPAIR REPLACE REVOKE

    ROLLBACK SAVEPOINT 'SELECT FROM table'

    'SET system variable' 'SET TRANSACTION'

    SHOW 'START TRANSACTION' TRUNCATE UPDATE

      在觸發器中也有完全一樣的限制.

      觸發器相對而言比較新,因此會有(bugs)缺陷.所以我在這里給大家警告,就像我在存儲過程書中所說那樣.不要在含有重要數據的數據庫中使用這個觸發器,如果需要的話在一些以測試為目的的數據庫上使用,同時在你對表創建觸發器時確認這些數據庫是默認的。

    Syntax 語法

      1. Syntax: Name 語法:命名規則


    CREATE TRIGGER <觸發器名稱> <--
    { BEFORE | AFTER }
    { INSERT | UPDATE | DELETE }
    ON <表名稱>
    FOR EACH ROW
    <觸發器SQL語句>

      觸發器必須有名字,最多64個字符,可能后面會附有分隔符.它和MySQL中其他對象的命名方式基本相象.

      這里我有個習慣:就是用表的名字+'_'+觸發器類型的縮寫.因此如果是表t26,觸發器是在事件UPDATE(參考下面的點(2)和(3))之前(BEFORE)的,那么它的名字就是t26_bu。


      2. Syntax: Time 語法:觸發時間


    CREATE TRIGGER <觸發器名稱>
    { BEFORE | AFTER } <--
    { INSERT | UPDATE | DELETE }
    ON <表名稱>
    FOR EACH ROW
    <觸發的SQL語句>

      觸發器有執行的時間設置:可以設置為事件發生前或后。

      3. Syntax: Event語法:事件


    CREATE TRIGGER <觸發器名稱>
    { BEFORE | AFTER }
    { INSERT | UPDATE | DELETE } <--
    ON <表名稱>
    FOR EACH ROW
    <觸發的SQL語句>

      同樣也能設定觸發的事件:它們可以在執行insert、update或delete的過程中觸發。

      4. Syntax: Table 語法:表


    CREATE TRIGGER <觸發器名稱>
    { BEFORE | AFTER }
    { INSERT | UPDATE | DELETE }
    ON <表名稱> <--
    FOR EACH ROW
    <觸發的SQL語句>

      觸發器是屬于某一個表的:當在這個表上執行插入、更新或刪除操作的時候就導致觸發器的激活.
    我們不能給同一張表的同一個事件安排兩個觸發器。

      5. Syntax: Granularity 語法:( :( 步長)觸發間隔


    CREATE TRIGGER <觸發器名稱>
    { BEFORE | AFTER }
    { INSERT | UPDATE | DELETE }
    ON <表名稱>
    FOR EACH ROW <--
    <觸發的SQL語句>

      觸發器的執行間隔:FOR EACH ROW子句通知觸發器每隔一行執行一次動作,而不是對整個表執行一次。

      6. Syntax: Statement 語法:語句


    CREATE TRIGGER <觸發器名稱>
    { BEFORE | AFTER }
    { INSERT | UPDATE | DELETE }
    ON <表名稱>
    FOR EACH ROW
    <觸發的SQL語句> <--

      觸發器包含所要觸發的SQL語句:這里的語句可以是任何合法的語句,包括復合語句,但是這里的語句受的限制和函數的一樣。

    Privileges權限

      你必須擁有相當大的權限才能創建觸發器(CREATE TRIGGER)。如果你已經是Root用戶,那么就足夠了。這跟SQL的標準有所不同,我也希望能盡快改成標準的。

      因此在下一個版本的MySQL中,你完全有可能看到有一種叫做CREATE TRIGGER的新權限。然后通過這樣的方法賦予:


    GRANT CREATE TRIGGER ON <表名稱> TO <用戶或用戶列表>;

      也可以通過這樣收回權限:


    REVOKE CREATE TRIGGER ON <表名稱> FROM <用戶或用戶列表>;

    Referring to OLD and NEW columns 關于舊的和新創建的列的標識

      在觸發器的SQL語句中,你可以關聯表中的任意列。但你不能僅使用列的名稱去標識,那會使系統混淆,因為那里可能會有列的新名(這可能正是你要修改的,你的動作可能正是要修改列名),還有列的舊名存在。因此你必須用這樣的語法來標識:

      "NEW . column_name"或者"OLD . column_name".這樣在技術上處理(NEW | OLD . column_name)新和舊的列名屬于創建了過渡變量("transition variables")。

      對于INSERT語句,只有NEW是合法的;對于DELETE語句,只有OLD才合法;而UPDATE語句可以在和NEW以及OLD同時使用。下面是一個UPDATE中同時使用NEW和OLD的例子。


    CREATE TRIGGER t21_au
    BEFORE UPDATE ON t22
    FOR EACH ROW
    BEGIN
    SET @old = OLD . s1;
    SET @new = NEW.s1;
    END;//

      現在如果t21表中的s1列的值是55,那么執行了"UPDATE t21 SET s1 = s1 + 1"之后@old的值會變成55,而@new的值將會變成56。

    Example of CREATE and INSERT CREATE和INSERT的例子

      CREATE table with trigger創建有觸發器的表

      這里所有的例程中我都假定大家的分隔符已經設置成//(DELIMITER //)。


    CREATE TABLE t22 (s1 INTEGER)//

    CREATE TRIGGER t22_bi
    BEFORE INSERT ON t22
    FOR EACH ROW
    BEGIN
    SET @x = 'Trigger was activated!';
    SET NEW.s1 = 55;
    END;//

      在最開始我創建了一個名字為t22的表,然后在表t22上創建了一個觸發器t22_bi,當我們要向表中的行插入時,觸發器就會被激活,執行將s1列的值改為55的動作。

      INSERT on table w ith a trigger使用觸發器執行插入動作


    mysql> INSERT INTO t22 VALUES (1)//

      讓我們看如果向表t2中插入一行數據觸發器對應的表會怎么樣?

      這里的插入的動作是很常見的,我們不需要觸發器的權限來執行它。甚至不需要知道是否有觸發器關聯。


    mysql> SELECT @x, t22.* FROM t22//
    +------------------------+------+
    | @x | s1 |
    +------------------------+------+
    | Trigger was activated! | 55 |
    +------------------------+------+
    1 row in set (0.00 sec)

      大家可以看到INSERT動作之后的結果,和我們預期的一樣,x標記被改動了,同時這里插入的數據不是我們開始輸入的插入數據,而是觸發器自己的數據。

    Example of a "check" constraint
    "check"完整性約束例子

      What's a "check" constraint 什么是"check"約束

      在標準的SQL語言中,我們可以在(CREATE TABLE)創建表的過程中使用"CHECK (condition)",
    例如:


    CREATE TABLE t25
    (s1 INT, s2 CHAR(5), PRIMARY KEY (s1),
    CHECK (LEFT(s2,1)='A'))
    ENGINE=INNODB;

      這里CHECK的意思是"當s2列的最左邊的字符不是'A'時,insert和update語句都會非法",MySQL的視圖不支持CHECK,我個人是很希望它能支持的。但如果你很需要在表中使用這樣的功能,我建議大家使用觸發器來實現。


    CREATE TABLE t25
    (s1 INT, s2 CHAR(5),
    PRIMARY KEY (s1))
    ENGINE=INNODB//

    CREATE TRIGGER t25_bi
    BEFORE INSERT ON t25
    FOR EACH ROW
    IF LEFT(NEW.s2,1)<>'A' THEN SET NEW.s1=0; END IF;//

    CREATE TRIGGER t25_bu
    BEFORE UPDATE ON t25
    FOR EACH ROW
    IF LEFT(NEW.s2,1)<>'A' THEN SET NEW.s1=0; END IF;//

      我只需要使用BEFORE INSERT和BEFORE UPDATE語句就行了,刪除了觸發器不會對表有影響,同時AFTER的觸發器也不能修改NEW的過程變量(transition variables)。為了激活觸發器,我執行了向表中的行插入s1=0的數據,之后只要執行符合LEFT(s2,1) <> 'A'條件的動作都會失敗:


    INSERT INTO t25 VALUES (0,'a') /* priming the pump */ //
    INSERT INTO t25 VALUES (5,'b') /* gets error '23000' */ //

    Don't Believe The Old MySQL Manual
    該拋棄舊的MySQL的手冊了

      我在這里警告大家不要相信過去的MySQL手冊中所說的了。我們已經去掉了關于觸發器的錯誤的語句,但是仍舊有很多舊版本的手冊在網上,舉個例子,這是一個德國的Url上的:


      http://dev.mysql.com/doc/mysql/de/ANSI_diff_Triggers.html.

      這個手冊上說觸發器就是存儲過程,忘掉吧,你也已經看見了,觸發器就是觸發器,而存儲過程還是存儲過程。

      手冊上還說觸發器可以從其他表上來刪除,或者是當你刪除一個事務的時候激發,無論他說的是什么意思,忘掉吧,MySQL不會去實現這些的。

      最后關于說使用觸發器會對查詢速度產生影響的說法也是錯的,觸發器不會對查詢產生任何影響。

      Bugs
      (不好的東西就不翻譯了)
      On December 14 2004, I did an "Advanced Search" in http://bugs.mysql.com for 'trigger' or
      'triggers', I found that there were 17 active bugs as of that date. Of course they might disappear
      before you read this, but just in case they haven't, I'll mention the important ones. If they're still
      there, you'll have to work around them when you're trying triggers.


      Bug#5859 DROP TABLE does not drop triggers.
      (刪除表的時候沒有自動刪除觸發器)
      When you drop a table, dropping the table's triggers should be automatic.


      Bug#5892 Triggers have the wrong namespace.
      (觸發器的命名空間有錯,你必須在前面加上表的名字才能刪除觸發器,下面是例子)
      You have to say "DROP TRIGGER <table name> . <trigger name>".
      The correct way is "DROP TRIGGER <trigger name>".


      Bug#5894 Triggers with altered tables cause corrupt databases.
      (觸發器對表的改變可能會造成數據庫數據被破壞)
      Do not alter a table that has a trigger on it, until you know this is fixed.

    posted @ 2007-12-03 07:19 jadmin 閱讀(88) | 評論 (0)編輯 收藏

      在數據庫表丟失或損壞的情況下,備份你的數據庫是很重要的。如果發生系統崩潰,你肯定想能夠將你的表盡可能丟失最少的數據恢復到崩潰發生時的狀態。有時,正是 MySQL 管理員造成破壞。管理員已經知道表已破壞,用諸如 vi 或 Emacs 等編輯器試圖直接編輯它們,這對表絕對不是件好事!

      備份數據庫兩個主要方法是用 mysqldump 程序或直接拷貝數據庫文件(如用 cp、cpio 或 tar 等)。每種方法都有其優缺點:

      mysqldump 與 MySQL 服務器協同操作。直接拷貝方法在服務器外部進行,并且你必須采取措施保證沒有客戶正在修改你將拷貝的表。如果你想用文件系統備份來備份數據庫,也會發生同樣的問題:如果數據庫表在文件系統備份過程中被修改,進入備份的表文件主語不一致的狀態,而對以后的恢復表將失去意義。文件系統備份與直接拷貝文件的區別是對后者你完全控制了備份過程,這樣你能采取措施確保服務器讓表不受干擾。

      mysqldump 比直接拷貝要慢些。

      mysqldump 生成能夠移植到其它機器的文本文件,甚至那些有不同硬件結構的機器上。直接拷貝文件不能移植到其它機器上,除非你正在拷貝的表使用 MyISAM 存儲格式。ISAM 表只能在相似的硬件結構的機器上拷貝。在 MySQL 3.23 中引入的 MyISAM 表存儲格式解決了該問題,因為該格式是機器無關的,所以直接拷貝文件可以移植到具有不同硬件結構的機器上。只要滿足兩個條件:另一臺機器必須也運行 MySQL 3.23 或以后版本,而且文件必須以 MyISAM 格式表示,而不是 ISAM 格式。

      不管你使用哪種備份方法,如果你需要恢復數據庫,有幾個原則應該遵守,以確保最好的結果:

      定期實施備份。建立一個計劃并嚴格遵守。

      讓服務器執行更新日志。當你在崩潰后需要恢復數據時,更新日志將幫助你。在你用備份文件恢復數據到備份時的狀態后,你可以通過運行更新日志中的查詢再次運用備份后面的修改,這將數據庫中的表恢復到崩潰發生時的狀態。

      以文件系統備份的術語講,數據庫備份文件代表完全傾倒(full dump),而更新日志代表漸進傾倒(incremental dump)。

      使用一種統一的和易理解的備份文件命名機制。象 backup1、buckup2 等不是特別有意義。當實施你的恢復時,你將浪費時間找出文件里是什么東西。你可能發覺用數據庫名和日期構成備份文件名會很有用。例如:

      %mysqldump samp_db >/usr/archives/mysql/samp_db.1999-10-02

      %mysqldump menagerie >/usr/archives/mysql/menagerie.1999-10-02

      你可能想在生成備份后壓縮它們。備份一般都很大!你也需要讓你的備份文件有過期期限以避免它們填滿你的磁盤,就象你讓你的日志文件過期那樣。

      用文件系統備份備份你的備份文件。如果遇上了一個徹底崩潰,不僅清除了你的數據目錄,也清除了包含你的數據庫備份的磁盤驅動器,你將真正遇上了麻煩。

      也要備份你的更新日志。

      將你的備份文件放在不同于用于你的數據庫的文件系統上。這將降低由于生成備份而填滿包含數據目錄的文件系統的可能性。

      用于創建備份的技術同樣對拷貝數據庫到另一臺機器有用。最常見地,一個數據庫被轉移到了運行在另一臺主機上的服務器,但是你也可以將數據轉移到同一臺主機上的另一個服務器。

      1 使用 mysqldump 備份和拷貝數據庫

      當你使用 mysqldumo 程序產生數據庫備份文件時,缺省地,文件內容包含創建正在傾倒的表的 CREATE 語句和包含表中行數據的 INSERT 語句。換句話說,mysqldump 產生的輸出可在以后用作 mysql 的輸入來重建數據庫。

      你可以將整個數據庫傾倒進一個單獨的文本文件中,如下:

      %mysqldump samp_db >/usr/archives/mysql/samp_db.1999-10-02

      輸出文件的開頭看起來象這樣:

      # MySQL Dump 6.0# # Host: localhost Database: samp_db#-------------

      --------------------------# Server version 3.23.2-alpha-log## Table st

      ructure for table absence#CREATE TABLE absence( student_id int(10)

      unsigned DEFAULT 0 NOT NULL, date date DEFAULT 0000-00-00 NOT NUL

      L, PRIMARY KEY (student_id,date));## Dumping data for table absence

      #INSERT INTO absence valueS (3,1999-09-03);INSERT INTO absence value

      S (5,1999-09-03);INSERT INTO absence valueS (10,1999-09-08);......

      文件剩下的部分有更多的INSERT和CREATE TABLE語句組成。如果你想壓縮備份,使用類似如下的命令:

      %mysqldump samp_db | gzip >/usr/archives/mysql/samp_db.1999-10-02.gz

      如果你要一個龐大的數據庫,輸出文件也將很龐大,可能難于管理。如果你愿意,你可以在 mysqldump 命令行的數據庫名后列出單獨的表名來傾到它們的內容,這將傾倒文件分成較小、更易于管理的文件。下例顯示如何將 samp_db 數據庫的一些表傾到進分開的文件中:

      %mysqldump samp_db student score event absence >grapbook.sql

      %mysqldump samp_db member president >hist-league.sql

      如果你生成準備用于定期刷新另一個數據庫內容的備份文件,你可能想用 --add- drop-table 選項。這告訴服務器將 DROP TABLE IF EXISTS 語句寫入備份文件,然后,當你取出備份文件并把它裝載進第二個數據庫時,如果表已經存在,你不會得到一個錯誤。

      如果你倒出一個數據庫以便能把數據庫轉移到另一個服務器,你甚至不必創建備份文件。要保證數據庫存在于另一臺主機,然后用管道傾倒數據庫,這樣 mysql 能直接讀取 mysqldump 的輸出。例如:你想從主機 pit- viper.snake.net 拷貝數據庫 samp_db 到 boa.snake.net,可以這樣很容易做到:

      %mysqladmin -h boa.snake.net create samp_db

      %mysqldump samp_db | mysql -h boa.snake.net samp_db

      以后,如果你想再次刷新 boa.snake.net 上的數據庫,跳過 mysqladmin 命令,但要對 mysqldump 加上--add-drop-table 以避免的得到表已存在的錯誤:

      %mysqldump --add-drop-table samp_db | mysql -h boa.snake.net samp_db

      mysqldump 其它有用的選項包括:

      --flush-logs 和 --lock-tables 組合將對你的數據庫檢查點有幫助。--lock-tables 鎖定你正在傾倒的所有表,而 --flush-logs 關閉并重新打開更新日志文件,新的更新日志將只包括從備份點起的修改數據庫的查詢。這將設置你的更新日志檢查點位備份時間。(然而如果你有需要執行個更新的客戶,鎖定所有表對備份期間的客戶訪問不是件好事。)

      如果你使用 --flush-logs 設置檢查點到備份時,有可能最好是傾倒整個數據庫。

      如果你傾倒單獨的文件,較難將更新日志檢查點與備份文件同步。在恢復期間,你通常按數據庫為基礎提取更新日志內容,對單個表沒有提取更新的選擇,所以你必須自己提取它們。

      缺省地,mysqldump 在寫入前將一個表的整個內容讀進內存。這通常確實不必要,并且實際上如果你有一個大表,幾乎是失敗的。你可用 --quick 選項告訴 mysqldump 只要它檢索出一行就寫出每一行。為了進一步優化傾倒過程,使用 --opt 而不是 --quick。--opt 選項打開其它選項,加速數據的傾倒和把它們讀回。

      用 --opt 實施備份可能是最常用的方法,因為備份速度上的優勢。然而,要警告你,--opt 選項確實有代價,--opt 優化的是你的備份過程,不是其他客戶對數據庫的訪問。--opt 選項通過一次鎖定所有表阻止任何人更新你正在傾倒的任何表。你可在一般數據庫訪問上很容易看到其效果。當你的數據庫一般非常頻繁地使用,只是一天一次地調節備份。

      一個具有 --opt 的相反效果的選項是 --dedayed。該選項使得 mysqldump 寫出 INSERT DELAYED 語句而不是 INSERT 語句。如果你將數據文件裝入另一個數據庫并且你想是這個操作對可能出現在該數據庫中的查詢的影響最小,--delayed 對此很有幫助。

      --compress 選項在你拷貝數據庫到另一臺機器上時很有幫助,因為它減少網絡傳輸字節的數量。下面有一個例子,注意到 --compress 對與遠端主機上的服務器通信的程序才給出,而不是對與本地主機連接的程序:

      %mysqldump --opt samp_db | mysql --compress -h boa.snake.net samp_db

      2 使用直接拷貝數據庫的備份和拷貝方法

      另一種不涉及 mysqldump 備份數據庫和表的方式是直接拷貝數據庫表文件。典型地,這用諸如 cp、tar 或 cpio 實用程序。本文的例子使用 cp。

      當你使用一種直接備份方法時,你必須保證表不在被使用。如果服務器在你則正在拷貝一個表時改變它,拷貝就失去意義。

      保證你的拷貝完整性的最好方法是關閉服務器,拷貝文件,然后重啟服務器。如果你不想關閉服務器,要在執行表檢查的同時鎖定服務器。如果服務器在運行,相同的制約也適用于拷貝文件,而且你應該使用相同的鎖定協議讓服務器“安靜下來”。

      假設服務器關閉或你已經鎖定了你想拷貝的表,下列顯示如何將整個 samp_db 數據庫備份到一個備份目錄(DATADIR 表示服務器的數據目錄):

      %cd DATADIR%cp -r samp_db /usr/archive/mysql

      單個表可以如下備份:

      %cd DATADIR/samp_db%cp member.* /usr/archive/mysql/samp_db%cp score.*

      /usr/archive/mysql/samp_db ....

      當你完成了備份時,你可以重啟服務器(如果關閉了它)或釋放加在表上的鎖定(如果你讓服務器運行)。

      要用直接拷貝文件把一個數據庫從一臺機器拷貝到另一臺機器上,只是將文件拷貝到另一臺服務器主機的適當數據目錄下即可。要確保文件是 MyIASM 格式或兩臺機器有相同的硬件結構,否則你的數據庫在另一臺主機上有奇怪的內容。你也應該保證在另一臺機器上的服務器在你正在安裝數據庫表時不訪問它們。

      3 復制數據庫(Replicating Database)

      復制(Replication)類似于拷貝數據庫到另一臺服務器上,但它的確切含義是實時地保證兩個數據庫的完全同步。這個功能將在 3.23 版中出現,而且還不很成熟,因此本文不作詳細介紹。

      4 用備份恢復數據

      數據庫損壞的發生有很多原因,程度也不同。如果你走運,你可能僅損壞一兩個表(如掉電),如果你倒霉,你可能必須替換整個數據目錄(如磁盤損壞)。在某些情況下也需要恢復,比如用戶錯誤地刪除了數據庫或表。不管這些倒霉事件的原因,你將需要實施某種恢復。

      如果表損壞但沒丟失,嘗試用 myisamchk 或 isamchk 修復它們,如果這樣的損壞可有修復程序修復,你可能根本不需要使用備份文件。

      恢復過程涉及兩種信息源:你的備份文件和個更新日志。備份文件將表恢復到實施備份時的狀態,然而一般表在備份與發生問題之間的時間內已經被修改,更新日志包含了用于進行這些修改的查詢。你可以使用日志文件作為 mysql 的輸入來重復查詢。這已正是為什么要啟用更新日志的原因。

      恢復過程視你必須恢復的信息多少而不同。實際上,恢復整個數據庫比單個表跟容易,因為對于數據庫運用更新日志比單個表容易。

      4.1 恢復整個數據庫

      首先,如果你想恢復的數據庫是包含授權表的 mysql 數據庫,你需要用 --skip -grant-table 選項運行服務器。否則,它會抱怨不能找到授權表。在你已經恢復表后,執行 mysqladmin flush-privileges 告訴服務器裝載授權標并使用它們。

      將數據庫目錄內容拷貝到其它某個地方,如果你在以后需要它們。

      用最新的備份文件重裝數據庫。如果你用 mysqldump 產生的文件,將它作為 mysql 的輸入。如果你用直接從數據庫拷貝來的文件,將它們直接拷回數據庫目錄,然而,此時你需要在拷貝文件之前關閉數據庫,然后重啟它。

      使用更新日志重復做備份以后的修改數據庫表的查詢。對于任何可適用的更新日志,將它們作為 mysql 的輸入。指定 --one-database 選項使得 mysql 只執行你有興趣恢復的數據庫的查詢。如果你知道你需要運用所有更新日志文件,你可以在包含日志的目錄下使用這條命令:

      % ls -t -r -1 update.[0-9]* | xargs cat | mysql --one-database db_name

      ls 命令生成更新日志文件的一個單列列表,根據服務器產生它們的次序排序(主意:如果你修改任何一個文件,你將改變排序次序,這導致更新日志一錯誤的次序被運用。)

      很可能你會是運用某幾個更新日志。例如,自從你備份以來產生的更新日志被命名為 update.392、update.393 等等,你可以這樣重新運行:

      %mysql --one-database db_name < update.392

      %mysql --one-database db_name < update.393

      .....

      如果你正在實施恢復且使用更新日志恢復由于一個錯誤建議的 DROP DATABASE、DROP TABLE 或 DELETE 語句造成丟失的信息,在運用更新日志之前,要保證從其中刪除這些語句。

      4.2 恢復單個表

      恢復單個表較為復雜。如果你用一個由 mysqldump 生成的備份文件,并且它不包含你感興趣的表的數據,你需要從相關行中提取它們并將它們用作 mysql 的輸入。這是容易的部分。難的部分是從只運用于該表的更新日志中拉出片斷。你會發覺 mysql_find_rows 實用程序對此很有幫助,它從更新日志中提取多行查詢。

      另一個可能性是使用另一臺服務器恢復整個數據庫,然后拷貝你想要的表文件到原數據庫中。這可能真的很容易!當你將文件拷回數據庫目錄時,要確保原數據庫的服務器關閉。

    posted @ 2007-12-03 07:17 jadmin 閱讀(89) | 評論 (0)編輯 收藏
    在Web應用中,經常需要動態生成圖片,比如實時股市行情,各種統計圖等等,這種情況下,圖片只能在服務器內存中動態生成并發送給用戶,然后在瀏覽器中顯示出來。

    本質上,瀏覽器向服務器請求靜態圖片如jpeg時,服務器返回的仍然是標準的http響應,只不過http頭的contentType不是text/html,而是image/jpeg而已,因此,我們在Servlet中只要設置好contentType,然后發送圖像的數據流,瀏覽器就能正確解析并顯示出圖片。

    在Java中,java.awt和java.awt.image包提供了基本的繪制圖像的能力,我們可以在內存中繪制好需要的圖形,然后編碼成jpeg或其他圖像格式,最后發送相應給瀏覽器即可。下面是使用Servlet動態創建圖像的詳細步驟:

    1.創建BufferedImage對象,該對象存在內存中,負責保存繪制的圖像;
    2.創建Graphics2D對象,該對象負責繪制所需的圖像;
    3.當繪制完成后,調用com.sun.image.codec.jpeg包的JPEG編碼器對其編碼;
    4.最后將編碼后的數據輸出至HttpResponse即可。

    注意com.sun.image.codec.jpeg包位于JDK目錄的rt.jar包中,它不是公開的API,需要將rt.jar復制到web應用程序的WEB-INF/lib下。

    我們先創建一個最簡單的Servlet:

    public class CreateImageServlet extends HttpServlet {
    ???? protected void doGet(HttpServletRequest request, HttpServletResponse response)
    ???????????? throws ServletException, IOException
    ???? {
    ???????? response.setContentType("image/jpeg");
    ???? }
    }


    我們首先設置了response的contentType為image/jpeg,這樣瀏覽器就可以正確識別。

    然后,創建一個大小為100x100的BufferedImage對象,準備繪圖:

    int width = 100;
    int height = 100;
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);


    接著,BufferedImage對象中獲取Graphics2D對象并繪圖:

    Graphics2D g = bi.createGraphics(); // 創建Graphics2D對象
    // 填充背景為白色:
    g.setBackground(Color.BLUE);
    g.clearRect(0, 0, width, height);
    // 設置前景色:
    g.setColor(Color.RED);
    // 開始繪圖:
    g.drawLine(0, 0, 99, 99); // 繪制一條直線
    // 繪圖完成,釋放資源:
    g.dispose();
    bi.flush();


    然后,對BufferedImage進行JPEG編碼:

    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
    JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
    param.setQuality(1.0f, false);
    encoder.setJPEGEncodeParam(param);
    try {
    ???? encoder.encode(bi);
    }
    catch(IOException ioe) {
    ???? ioe.printStackTrace();
    }


    編碼后的JPEG圖像直接輸出到了out對象中,我們只要傳入response. getOutputStream()就可以直接輸出到HttpResponse中。

    下面是完整的代碼:

    package com.crackj2ee.web.util;

    import java.io.*;
    import java.awt.*;
    import java.awt.image.*;

    import javax.servlet.*;
    import javax.servlet.http.*;

    import com.sun.image.codec.jpeg.*;

    /**
    * @author Liao Xue Feng
    */
    public class CreateImageServlet extends HttpServlet {

    ???? protected void doGet(HttpServletRequest request, HttpServletResponse response)
    ???????????? throws ServletException, IOException
    ???? {
    ???????? response.setContentType("image/jpeg");
    ???????? createImage(response.getOutputStream());
    ???? }

    ???? private void createImage(OutputStream out) {
    ???????? int width = 100;
    ???????? int height = 100;
    ???????? BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    ???????? Graphics2D g = bi.createGraphics();
    ???????? // set background:
    ???????? g.setBackground(Color.BLUE);
    ???????? g.clearRect(0, 0, width, height);
    ???????? // set fore color:
    ???????? g.setColor(Color.RED);
    ???????? // start draw:
    ???????? g.drawLine(0, 0, 99, 199);
    ???????? // end draw:
    ???????? g.dispose();
    ???????? bi.flush();
    ???????? // encode:
    ???????? JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
    ???????? JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
    ???????? param.setQuality(1.0f, false);
    ???????? encoder.setJPEGEncodeParam(param);
    ???????? try {
    ???????????? encoder.encode(bi);
    ???????? }
    ???????? catch(IOException ioe) {
    ???????????? ioe.printStackTrace();
    ???????? }
    ???? }
    }


    最后將這個Servlet編譯,注冊到web.xml中,映射路徑為/CreateImage,寫一個簡單的index.html測試:

    <html><head></head>
    <body>
    <img src="CreateImage">
    </body></html>


    如能正確顯示,大功告成!
    posted @ 2007-12-02 17:11 jadmin 閱讀(85) | 評論 (0)編輯 收藏
      在任何一個綜合性網站,我們往往需要上傳一些圖片資料。但隨著高分辨率DC的普及,上傳的圖片容量會很大,比如300萬象素DC出來的文件基本不下600K。為了管理方便,大家可能不愿意每次都用ACDsee修改它,而直接上傳到服務器。但是這種做法在客戶端看來就沒有那么輕松了,對于撥號上網的用戶簡直是一場惡夢,雖然你可以在圖片區域設置wide和high!
      
      問題的解決之道來了!我們可以在類中處理一張大圖,并縮小它。
      前提是需要JDK1.4,這樣才能進行處理。按以下方法做:
      
      import java.io.File;
      import java.io.FileOutputStream;
      import java.awt.Graphics;
      import java.awt.Image;
      import java.awt.image.BufferedImage;
      
      import com.sun.image.codec.jpeg.JPEGCodec;
      import com.sun.image.codec.jpeg.JPEGImageEncoder;
      
      public class JpgTest {
      
      public void JpgTset() throws Exception{
      File _file = new File("/Order005-0001.jpg"); //讀入文件
      Image src = javax.imageio.ImageIO.read(_file); //構造Image對象
      int wideth=src.getWidth(null); //得到源圖寬
      int height=src.getHeight(null); //得到源圖長
      BufferedImage tag = new BufferedImage(wideth/2,height/2,BufferedImage.TYPE_INT_RGB);
      tag.getGraphics().drawImage(src,0,0,wideth/2,height/2,null); //繪制縮小后的圖
      FileOutputStream out=new FileOutputStream("newfile.jpg"); //輸出到文件流
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      encoder.encode(tag); //近JPEG編碼
      //System.out.print(width+"*"+height);
      out.close();
      }
      }
      
      過程很簡單,從本地磁盤讀取文件Order005-0001.jpg(2032*1524),變成Image對象src,接著構造目標文件tag,設置tag的長寬為源圖的一半,對tag進行編碼,輸出到文件流out,最后關閉文件流。
      
      還有一些問題需要說明:
      第一,目前只能支持JPG(JPEG)、GIF、PNG三種格式。
      第二,對于源圖的容量有限制,最好不要超過1M,否則會拋內存不足的錯誤,不過我試驗過1.8M的源圖,可以成功,但是也很容易拋內存不足。
      
      引用一位前輩的話:圖象運算本身是密集型運算,需要大量的內存存放象素值。我用VC試了一下,4M的圖象也有問題,而且越是壓縮比大的圖片在內存中還原成BITMAP時需要的內存越大。解決的方法,可以重寫編碼類,先開一定的內存,然后一段一段編碼寫到臨時文件中,輸出的時候再一段一段讀出來。或利用nio的內存映象來操作。JavaMail由于采用了Builder模式,先生成一個郵件的每一個部分,然后合并成一個完整的郵件對象,這樣每個構件都要先生成到內存中,你如果發送一個上百兆的附件,那么在構造Part時肯定內存溢出,所以我就改寫了BodyPart的構造,讓他和一個臨時文件關聯,然后用臨時文件保存Part而不是構造在內存中,這樣任義大小的附件(硬盤能放得下為限)都可以發送了。
      
      最后,如果大家對圖像處理有更高的要求,不妨關注一下開源項目。比如JMagick,可以使用JMagick來實現圖片的復制、信息獲取、斜角、特效、組合、改變大小、加邊框、旋轉、切片、改變格式、去色等等功能。
    posted @ 2007-12-02 17:07 jadmin 閱讀(57) | 評論 (0)編輯 收藏
     有許多標準和實踐準則可適用于Java開發者,但此處要說的,是每個Java開發者需堅守的基本原則。

      一、為代碼加注釋。雖然每個人都知道這點,但有時卻不自覺忘了履行,今天你“忘了”加注釋了嗎?雖然注釋對 程序的功能沒什么“貢獻”,但過一段時間,比如說兩星期之后或者更長,回過頭來看看自己的代碼,說不定已經記不住它是干什么的了。如果這些代碼是你個人 的,那還算是走運了,不幸的是,當然了,大多數時候都是別人的不幸,很多時候大家都是在為公司寫代碼,寫代碼的人也許早已經離開了公司,但別忘了一句古 話,有來有往嘛,為他人,也為我們自己,請為你的代碼加上注釋。

      二、不要讓事情復雜化。程序員有時候總是對簡單問題想出復雜的解決方案,比如說,在只有五個用戶的程序中引 入EJB、對程序實現了并不需要的框架(framework),之類的還有屬性文件、面向對象解決方案、多線程等等。為什么要這樣做呢?也許我們并不知道 是否這樣會更好,但這樣做也許可以學到一些新東西,或者讓自己更感興趣一些。如果是不知道為什么這樣做,建議多請教經驗豐富的程序員,如果是為了個人的目 的,麻煩讓自己更專業一點。

      三、始終牢記——“少即是好(Less is more)并不總是對的”。代碼效率雖然很重要,但在許多解決方案中,編寫更少的代碼并不能改善這些代碼的效率,請看下面這個簡單的例子:


    if(newStatusCode.equals("SD") && (sellOffDate == null ||
    todayDate.compareTo(sellOffDate)<0 || (lastUsedDate != null &&
    todayDate.compareTo(lastUsedDate)>0)) ||
    (newStatusCode.equals("OBS") && (OBSDate == null ||
    todayDate.compareTo(OBSDate)<0))){
    newStatusCode = "NYP";
    }

      能看明白if條件語句是干什么的嗎?能想出來是誰寫的這段代碼嗎?如果把它分成兩段獨立的if語句,是不是更容易理解呢,下面是修改后的代碼:


    if(newStatusCode.equals("SD") && (sellOffDate == null ||
    todayDate.compareTo(sellOffDate)<0 || (lastUsedDate != null &&
    todayDate.compareTo(lastUsedDate)>0))){
    newStatusCode = "NYP";
    }else
    if(newStatusCode.equals("OBS") && (OBSDate == null ||
    todayDate.compareTo(OBSDate)<0))
    {
    newStatusCode = "NYP";
    }

      是不是讀起來容易多了呢,在此只是多加了一個if和兩個花括號,但代碼的可讀性與可理解性就一下子提高了一大截。

      四、請不要硬編碼。開發者經常有意“忘記”或忽略掉這點,因為有些時候開發日程逼得實在太緊。其實,多寫一行定義靜態變量的代碼能花多少時間呢?


    public class A {
    public static final String S_CONSTANT_ABC = "ABC";
    public boolean methodA(String sParam1){
    if (A.S_CONSTANT_ABC.equalsIgnoreCase(sParam1)){
    return true;
    }
    return false;
    }
    }

      現在,每次需要將“ABC”與其他變量進行比較時,不必記住實際代碼,直接引用A.S_CONSTANT_ABC就行了,而且在今后需要進行修改時,也可在一處修改,不會翻遍整個源代碼逐個修改了。

      五、不要“創造”自己的框架(framework)。確切來說,有數以千計的各種框架存在,而且大多數是開 源的,這些框架都是優秀的解決方案,可用于日常程序開發中,我們只需使用這些框架的最新版本就行了,至少表面上要跟上形勢吧。被大家廣為接受的最為明顯的 一個例子就是Struts了,這個開源web框架非常適合用在基于web的應用程序中。是不是想開發出自己的Struts呢,還是省點力氣吧,回頭看看第 二條——不要讓事情復雜化。另外,如果正在開發的程序只有3個窗口,就不要使用Struts了,對這種程序來說,不需要那么多的“控制”。

      六、不要使用println及字符串連接。通常為了調試方便,開發者喜歡在可能的所有地方都加上 System.out.println,也許還會提醒自己回過頭來再來刪除,但有些時候,經常會忘了刪除或者不愿意刪除它們。既然使用 System.out.println是為了測試,那么測試完之后,為什么還要留著它們呢,因為在刪除時,很可能會刪除掉真正有用的代碼,所以不能低估 System.out.println危害啊,請看下面的代碼:


    public class BadCode {
    public static void calculationWithPrint(){
    double someValue = 0D;
    for (int i = 0; i < 10000; i++) {
    System.out.println(someValue = someValue + i);
    }
    }
    public static void calculationWithOutPrint(){
    double someValue = 0D;
    for (int i = 0; i < 10000; i++) {
    someValue = someValue + i;
    }
    }
    public static void main(String [] n) {
    BadCode.calculationWithPrint();
    BadCode.calculationWithOutPrint();
    }
    }

      從測試中可以發現,方法calculationWithOutPrint()執行用了0.001204秒,作為對比,方法calculationWithPrint()執行可是用了10.52秒。

      要避免浪費CPU時間,最好的方法是引入像如下的包裝方法:


    public class BadCode {
    public static final int DEBUG_MODE = 1;
    public static final int PRODUCTION_MODE = 2;
    public static void calculationWithPrint(int logMode){
    double someValue = 0D;
    for (int i = 0; i < 10000; i++) {
    someValue = someValue + i;
    myPrintMethod(logMode, someValue);
    }
    }
    public static void myPrintMethod(int logMode, double value) {
    if (logMode > BadCode.DEBUG_MODE) { return; }
    System.out.println(value);
    }
    public static void main(String [] n) {
    BadCode.calculationWithPrint(BadCode.PRODUCTION_MODE);
    }
    }


      另外,字符串連接也是浪費CPU時間的一個大頭,請看下面的示例代碼:


    public static void concatenateStrings(String startingString) {
    for (int i = 0; i < 20; i++) {
    startingString = startingString + startingString;
    }
    }
    public static void concatenateStringsUsingStringBuffer(String startingString) {
    StringBuffer sb = new StringBuffer();
    sb.append(startingString);
    for (int i = 0; i < 20; i++) {
    sb.append(sb.toString());
    }
    }

      在測試中可發現,使用StringBuffer的方法只用了0.01秒執行完畢,而使用連接的方法則用了0.08秒,選擇顯而易見了。

      七、多關注GUI(用戶界面)。再三強調,GUI對商業客戶來說,與程序的功能及效率同等重要,GUI是一 個成功程序的最基本部分,而很多IT經理往往都沒注意到GUI的重要性。在現實生活中,許多公司可能為了節省開支,沒有雇用那些有著設計“用戶友好”界面 豐富經驗的網頁設計者,此時Java開發者只能依賴他們自身的HTML基本功及在此領域有限的知識,結果,很多開發出來的程序都是“計算機友好”甚于“用 戶友好”。很少有開發者同時精通軟件開發及GUI設計,如果你在公司“不幸”被分配負責程序界面,就應該遵守下面三條原則:

      1、 不要再發明一次輪子,即不做無用功。現有的程序可能會有類似的界面需求。

      2、 先創建一個原型。這是非常重要一步,用戶一般想看到他們將使用的東西,而且可以先利用這個原型征求用戶的意見,再慢慢修改成用戶想要的樣子。

      3、 學會換位思考。換句話來說,就是從用戶的角度來審查程序的需求。舉例來講,一個匯總的窗口可以跨頁或者不跨頁,作為一個軟件開發者,可能會傾向于不跨頁,因為這樣簡單一些。但是,從用戶的角度來看,可能不希望看到上百行數據都擠在同一頁上。

      八、文檔需求不放松。每個商業需求都必須記錄在案,這可能聽上去像童話,似乎在現實生活中很難實現。而我們要做的是,不管開發時間多緊迫,不管最終期限多臨近,對每個商業需求都必須記錄在案。

      九、單元測試、單元測試、單元測試。關于什么是單元測試的最好方法,在此不便細說,只是強調,單元測試一定要完成,這也是編程中最基本的原則。當然了,如果有人幫你做單元測試自然是最好,如果沒有,就自己來做吧,當創建一個單元測試計劃時,請遵守以下三條最基本的原則:

      1、 先于編寫類代碼之前編寫單元測試。

      2、 記錄單元測試中的代碼注釋。

      3、 測試所有執行關鍵功能的公有方法,這里不是指set和get方法,除非它們是以自己獨特方式執行set和get方法。

      十、質量,而不是數量。有些時候因為產品問題、期限緊迫、或一些預料之外的事情,導致常常不能按時下班,但一般而言,公司不會因為雇員經常加班而對之表揚和獎勵,公司只看重高質量的工作。如果遵守了前九條原則,你會發現自己寫出的代碼bug少且可維護性高,無形中質量提高了一大步。
    posted @ 2007-12-02 01:26 jadmin 閱讀(74) | 評論 (0)編輯 收藏

    http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=833

    posted @ 2007-12-01 17:09 jadmin 閱讀(62) | 評論 (0)編輯 收藏

    第一種方法:在tomcat中的conf目錄中,在server.xml中的,<host/>節點中添加:
    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true">
    </Context>
    至于Context 節點屬性,可詳細見相關文檔。

    第二種方法:將web項目文件件拷貝到webapps 目錄中。

    第三種方法:很靈活,在conf目錄中,新建 Catalina(注意大小寫)\localhost目錄,在該目錄中新建一個xml文件,名字可以隨意取,只要和當前文件中的文件名不重復就行了,該xml文件的內容為:
    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true">
    </Context>

    posted @ 2007-12-01 15:30 jadmin 閱讀(48) | 評論 (0)編輯 收藏

    1、前言:

    CVS是版本控制的利器,目前在Linux和Windows下都有不同版本;但是國內大多數應用介紹都是基于Linux等開放源代碼的開放性軟件組織,而且講解的也不系統,讓人摸不著頭腦;Windows下的CVS使用介紹更是了了無幾。
    本文是針對Windows的LAN環境下使用CVS的經驗介紹,一步一步的向您介紹如何配置和使用CVS的服務器端和客戶端。同時,本文只使用到了CVS當中最基本的東西,還有很多更為高級的東西,本文暫不涉及。下面是本文的另一個連接映射,歡迎大家討論使用,共同進步。
    文章連接http://www.kuihua.net/book/list.asp?id=66
    論壇連接http://www.kuihua.net/bbs/dispbbs.asp?boardID=1&;RootID=670&ID=670

    2、安裝版本:

    2.1、服務器端(CVSNT)

    1. 本文使用的是CVSNT-2.0.4,這是一個比較穩定的版本,不要使用最新的CVSNT-2.1.1,本人在使用中發現有比較嚴重的Bug。
    2. 下載連接http://www.cvsnt.org 目前,它提供2.0.6和2.1.1版本的下載。
    3. 上面連接還提供源代碼,有興趣的朋友還可以下載下來仔細研究:)。
    4. 有心的朋友,仔細觀察就會發現http://www.cvsnt.org 并沒有提供任何客戶端的下載,那是因為CVS.exe既可以用于服務器端又可以用于客戶端,WinCVS是為了客戶端使用的方便而定制的外殼。(關于這一點,本人未得到任何證實,只是本人在使用過程中的一種體會,歡迎大家討論。)

    2.2、客戶端(WinCVS)

    1. 本文使用的是WinCVS-1.3b13,這應該是一個最新版本:),本人在使用過程中并沒有發現有任何嚴重的Bug。
    2. 下載連接http://sourceforge.net/projects/cvsgui/
    3. 此網站還提供豐富的CVS文檔和相關源代碼,以及多個OS下面的相關文檔和代碼;有收藏癖的朋友有福了:)。
    4. WinCVS-1.3b13 使用的CVSNT的版本是CVSNT-2.0.2,在與服務器端的CVSNT-2.0.4 版本配合使用時,未發現任何不兼容或沖突現象。
    5. 在本人的系統中用cvs version命令顯示的結果如下:
    Client: Concurrent Versions System (CVSNT) 2.0.2 (client/server)
    Server: Concurrent Versions System (CVSNT) 2.0.4 (client/server)

    3、服務器端(CVSNT)的安裝與配置:

    3.1、服務器端機器和環境配置:

    1. 操作系統:Windows 2000 Professional SP2中文版
    2. 機器名稱:Server
    3. 機器地址:192.168.0.6 (內部IP)
    4. 網絡環境:100兆交換局域網
    5. 硬盤分區格式:FAT32與NTFS都可以。
    6. 準備2個CVSNT的工作目錄:
    F:\KHRoot (存放自己源代碼的根目錄)
    F:\KHTemp (存放CVS出錯信息的目錄)
    7. 本機上存在有的用戶列表:(由NT或本機的使用者創建)
    Administrator (系統管理員)
    Jackey (普通用戶)
    Goury (普通用戶)
    Riolee (普通用戶)

    3.2、安裝CVSNT:

    1. 下載CVSNT-2.0.4;使用administrator登陸到Server機器上。
    2. 雙擊自解壓的exe文件,選擇Full Install,其它按照默認方式安裝;安裝完畢后可以在服務控制器中發現多了2個服務:cvsnt與cvslocking
    3. 發送Service Control Panel到桌面,形成快捷方式。
    4. 安裝程序會自動將CVS安裝路徑,設置到系統的Path環境變量當中,因此使用者可以在控制臺(cmd)中任意位置執行cvs.exe,這一點對下面的配置很重要!!

    3.3、配置CVSNT服務器:

    1. 雙擊Service Control Panel快捷方式,在Service Status頁面,確認2個服務正常和穩定運行。
    2. 選擇Repository頁面,點按Add按鈕,選擇已經準備好的F:\KHRoot這個目錄,確認,OK,Yes,這時會在F:\KHRoot下面建立CVSRoot目錄,這是CVS默認的管理目錄(默認模塊)。如果報錯,那是系統Path路徑未設置正確。
    3. 選擇Advanced頁面,勾上Use local users for pserver ...,(Why? I don't know!J),在Temporary欄選擇已經準備好的F:\KHTemp,確認,OK。
    4. 點按【應用】按鈕,確認,退出,OK,搞定!!

    3.4、小結:

    1. 至此,CVSNT服務器端基本配置完畢,下面進行WinCVS的使用和管理。
    2. 由于CVS支持遠程管理,也就是客戶端與服務器集成的特性,因此,我們將添加用戶、權限控制、模塊維護等所有的管理工作都放到遠端(WinCVS)進行管理,服務器端這時可以Ctrl+Atl+Del進入鎖定狀態了,下面的所有工作都交給遠端的WinCVS來進行管理。

    4 客戶端(WinCVS)的安裝與配置:

    4.1 客戶端機器和環境配置:

    1. 操作系統:Windows 2000 Professional SP2中文版
    2. 機器名稱:YCW2000
    3. 機器地址:192.168.0.2 (內部IP)
    4. 網絡環境:100兆交換局域網,可以直接訪問到Server
    5. 硬盤分區格式:FAT32與NTFS都可以。

    4.2 安裝WinCVS:

    1. 下載WinCVS 1.3b13,全部按照默認安裝就可以了。
    2. 啟動WinCVS,開始使用。特別注意:以下的所有操作都是在YCW2000(192.168.0.2)這臺機器上遠程完成的,此時的Server(192.168.0.6)主機已經進入了鎖定狀態。

    5 管理員使用WinCVS進行遠程管理:

    5.1 配置WinCVS成管理員狀態:

    1. 準備管理員工作目錄:(在YCW2000機器上)
    E:\CVSClient\Admin (管理員工作目錄)
    E:\CVSTemp (WinCVS臨時目錄)
    2. 第一次啟動WinCVS時會自動彈出Preferences配置頁面,也可以通過Admin=>Preference菜單進入;第一次使用時需要配置如下的3個頁面:
    General頁面設置:
    注:按照圖示方式輸入即可,需要注意的是Path部分的格式是Unix路徑格式,它指的是CVSNT端設置的工作根目錄。
    CVS頁面設置: 注:Home路徑是設置密碼等文件的存放位置,必須指定,否則在登陸時,WinCVS也要彈出設置框。這個Home路徑需要Python.exe這個外掛程序才有效。這里選擇已經準備好的路徑:E\CVSTemp
    WinCVS頁面設置:
    注:此頁面設置WinCVS的外掛編輯程序,通常使用UltraEdit比較方便。
    3. 設置管理員的工作路徑:可以點按圖標 ,或View=>Browse Location=>Change...菜單進行設置,選擇已經準備好的路徑:E:\CVSClient\Admin,確認,OK,這時此目錄將出現在WinCVS的左邊導航欄【Workspace】內。
    4. 至此,WinCVS就被配置成了遠程的管理員使用狀態,下面進行一般管理員需要的基本操作演練。演練的內容為:Administrator需要管理Jackey,Goury,Riolee三個用戶,分別為這3個用戶建立工作目錄,每個人只能訪問自己的工作目錄。同時,只有Administrator能夠進行權限分配,其它人沒有任何管理權限。

    5.2 管理員進行管理演練:

    1. 登陸遠程CVSNT:
      ◇ 選擇Admin=>Login菜單,默認設置,OK。
      ◇ 彈出密碼輸入框,確認,OK。注意觀察輸出框【OutPut】的返回結果。
    2. Checkout默認模塊:(CVSRoot管理模塊)
      ◇ 在左邊導航欄【Workspace】內,選擇【Admin】點按右鍵,選擇【Checkout
      modules...】,在【Checkout settings】中輸入CVSRoot,確定,OK。如下圖:
      ◇ 如果成功的話,會在【Admin】欄下增加一個【CVSRoot】目錄。表示您已經將【
      CVSRoot】這個管理模塊下載到本地了。

    3. CVS中目錄權限介紹:

    ◇ 系統支持的目錄權限列表:
    r (讀取權限)
    w (寫入權限)
    c (創建和刪除權限)
    n (沒有任何權限)
    ◇ 默認情況下,任何用戶都擁有任何目錄的所有權限。
    ◇ 任何情況下只有目錄的擁有者和Administrator才有權力更改目錄的使用權限。下面將會介紹如何修改目錄權限和目錄的擁有者。

    4. 修改CVSRoot的權限:只讓Administrator擁有rcw三種全部權限。

    ◇ 選中剛剛下載的【CVSRoot】模塊,【Ctrl+L】或Admin=>Command Line...,彈出Command Line Settings對話框,直接執行CVS命令。
    ◇ 取消所有用戶的默認權限:cvs chacl default:n 回車,OK,完成。
    ◇ 設置Administrator擁有所有權限:cvs chacl administrator:rcw 回車,OK,完成。
    ◇ 更改【CVSRoot】的擁有者:cvs chown administrator 回車,OK,完成。
    ◇ 查看【CVSRoot】的權限狀態:cvs lsacl 回車,OK,在【Output】中顯示:
    Owner: administrator
    default:n
    administrator:rwc
    ◇【CVSRoot】的權限配置完畢。

    5. 編寫代碼庫中的模塊文件,便于多用戶下載方便。

    l 需要自己編寫的模塊文件格式如下:(實現基本功能)
    【模塊一的描述】【n個空格或參數】【相對目錄路徑一(DirA)】
    【模塊二的描述】【n個空格或參數】【相對目錄路徑二(DirB)】
    ......
    【模塊X的描述】【n個空格或參數】【相對目錄路徑X(DirX)】
    ◇【描述信息】與【相對路徑】在字面上不一致時,需要使用-a參數。
    ◇ 【相對路徑】指的是CVS會自動帶上根路徑,這里是F:\KHRoot,所以上面例子的完整路徑為:F:\KHRoot\DirA
    ◇ 了解了模塊文件結構,現在來實際操作一把:雙擊【CVSRoot】模塊下的modules文件,用UltraEdit打開進行編輯。
    ◇ 為Jackey,Goury,Riolee三個用戶分配工作目錄和完成其它模塊描述。
    CVSRoot CVSRoot
    Jackey工作目錄 -a Jackey
    Goury工作目錄 -a Goury
    Riolee工作目錄 -a Riolee
    ◇ 編輯完畢,存盤。回到WinCVS,選中modules這個文件【Ctlr+M】或右鍵選擇【Commit selection...】,默認設置,【確認】,OK,完成上傳。

    6 . 為三個用戶分別上傳工作目錄:

    ◇ 在YCW2000機器上的E:\CVSClient\Admin分別建立三個目錄分別名為:Jackey,Goury,Riolee,作為臨時交換目錄。
    ◇ 在新創建的每個目錄中用UltraEdit或拷貝一個Readme.txt作為引子文件!!:)
    ◇ 然后,回到WinCVS,在【Workspace】欄的【Admin】目錄下形成如下的目錄結構:
    ◇ 分別選中Goury,Jackey,Riolee,右鍵,點按【Import Module】,選擇【Continue】,其它全部使用默認值,【確定】,OK,完成上傳工作。
    ◇ 仔細觀察【Output】窗口,確認都成功上傳了。
    ◇ 轉移到系統的Explore程序中,刪除E:\CVSClient\Admin目錄下的Jackey,Goury,Riolee三個目錄。然后回到WinCVS當中。(一定要刪除!!!:)
    ◇ 至此,就完成了工作目錄的上傳工作。

    7. 【Checkout】下載3個用戶的工作目錄到【Admin】目錄下:

    ◇ 在【Workspace】欄選擇【Admin】目錄,右鍵,點按【Checkout Module...】,如下圖:
    ◇ 選擇【...】,得到CVSNT上最新的模塊配置情況,彈出如下的信息框:
    ◇ 這個結構圖就是剛才在modules當中編寫的模塊文件格式信息。選擇【Jackey工作目錄】,下載到YCW2000機器的E:\CVSClient\Admin目錄下。
    ◇ 按照以上操作,依次分別下載【Goury工作目錄】和【Riolee工作目錄】。形成如下狀態:


    8. 為三個用戶分別設置各自目錄的訪問權限。(只有自己才能訪問自己的工作目錄)

    ◇ 選中【Goury】目錄,【Ctrl+L】或Admin=>Command Line...,彈出Command Line Settings對話框,直接執行CVS命令。
    ◇ 取消所有用戶的默認權限:cvs chacl default:n 回車,OK,完成。
    ◇ 設置Goury擁有所有權限:cvs chacl goury:rcw 回車,OK,完成。
    ◇ 查看【CVSRoot】的權限狀態:cvs lsacl 回車,OK,在【Output】中顯示:
    Owner: administrator
    default:n
    goury:rwc
    ◇ 按照以上的方法依次分別設置【Jackey】與【Riolee】的工作目錄訪問權限。
    ◇ 至此,完成了3個用戶的目錄權限分配。注意,雖然Administrator也沒有權力再次【Checkout】那3個用戶的工作目錄,但是它是這些目錄的擁有者又是Administrator,因此,只有它才有權力更改這些目錄的訪問權限。

    9. CVSNT系統中的用戶管理原則:

    ◇ CVSNT的用戶與本機(這里是Server機器)上的NT用戶是相關聯的,即CVSNT用的全是本機上存在的已有用戶,因此在默認情況下可以不用設置任何用戶名,只要使用本機上已經存在的用戶名就可以用WinCVS進行登陸。
    ◇ 只有用Administrator身份登陸到CVSNT系統中,才有權力進行新用戶的創建和刪除。
    ◇ 使用CVS創建的新用戶,必須與服務器端機器上的NT用戶相綁定,才能生效;因此,這個新用戶實際上是綁定它的NT用戶的一個替身,在CVS系統中稱為"別名"。
    ◇ 一個NT用戶可以有多個‘替身'或‘別名',充當多個CVS用戶。

    10. 用WinCVS進行新用戶的添加和刪除。(確保使用Administrator登陸)

    ◇ 【Ctrl+L】或Admin=>Command Line...,彈出Command Line Settings對話框,直接執行CVS命令。
    ◇ 添加新用戶【Killer】:cvs passwd -a Killer 回車,設置密碼,OK,完成。
    ◇ 綁定【Killer】到【Jackey】:cvs passwd -r Jackey Killer 回車,設置密碼,OK,完成。
    ◇ 兩次輸入的密碼可以不同,但以第二次輸入的密碼為最終密碼。
    ◇ 刪除用戶【Killer】:cvs passwd -X Killer 回車,OK,完成。
    ◇ 其它特殊的功能查看passwd命令的幫助。

    11.使用完畢后,一定要【Logout】,因為WinCVS退出時并不會自動注銷自己在遠端的會話;這樣做是為了防止其它人接著打開WinCVS,不用登陸就可以完成你能進行的所有操作了。

    6 WinCVS中常見的特殊操作:

    6.1 如何刪除下載的文件或目錄:

    1. 選中下載的某個或多個文件,執行【Remove】命令。
    2. 再次選中這些文件,執行【Commit】命令就完成了刪除文件的操作。
    3. 本質上CVS是不會刪除任何文件和目錄的,即使是執行了以上操作,刪除了某些文件,遠端CVS實際執行的是將提交刪除的文件放到了一個叫【Attic】的目錄下,因此,這些被刪除的文件是可以通過一定的方法恢復的。

    6.2 如何恢復已經刪除的文件或目錄:

    1. 在執行了【Remove】命令之后恢復文件。
    ◇ 【Ctrl+L】直接輸入命令cvs add xxxxx,或執行【Add Selection】界面操作。
    ◇ 這樣就可以直接恢復還未提交的刪除文件。
    2. 在執行了【Commit】命令之后恢復文件。
    ◇ 只能用【Ctrl+L】直接輸入命令cvs add xxxxx,這時會得到一個空的文件。
    ◇ 選中這個空文件,執行【Update】操作,得到這個文件的實體。
    ◇ 再次選中這個文件,執行【Commit】操作,得到這個文件最新版本。
    3. 由于CVS系統中本質上不會刪除任何目錄,因此,談不上對目錄的恢復,但是CVS系統默認情況下是要在用戶本機上(如:YCW2000)要刪除空目錄,因此,可以用如下方法得到已被刪除的空目錄:cvs checkout -p xxx,也可以在Admin=>Preference的【Globals】頁面進行設置。
    4. 可見,CVS系統能夠保證:只要上傳到服務器的文件,無論你怎么在遠程進行如何的操作,正常的或非正常的操作,都可以用Administrator登陸上去,通過以上的方法找到丟失的文件。除非用戶進入到遠端服務器,將文件手動刪除,那就沒辦法了:)

    6.3 如何得到以前版本的文件:

    1. 有時我們需要得到以前版本的文件,而WinCVS默認方式只傳遞最新的版本。
    2. 選中某個文件,【Ctrl+G】或右鍵,點按【Graph selection...】,使用默認設置,就可以得到該文件所以版本的圖形結構描述。
    3. 選中一個版本,右鍵,點按【Retrieve revision】,就可以得到相應的老版本文件。當然也可以得到最新版本的文件:)

    6.4 有時WinCVS會變得異常緩慢,怎么辦?

    1. 確認安裝了WinCVS的機器上沒有安裝CVSNT服務器端,因為它們使用的版本有可能不一致。
    2. 只安裝了WinCVS,但以前安裝過其它版本的WinCVS,怎么辦?
    3. 先卸載所有的WinCVS系統,刪除安裝目錄下的殘留文件。
    4. 打開注冊表編輯器,全程查找cvs關鍵字,找到一個刪除一個,一直到找不到為止!!:)
    5. 重新安裝WinCVS,這個問題基本上就可以解決了,我就是這樣解決,不曉得你那里如何?:)

    7 其它說明:

    1. 本文的重點在介紹如何讓使用者搭建CVSNT+WinCVS這個系統,因此重點介紹了管理員的常用操作,至于一般用戶使用到的操作,相對比較簡單和單一,使用WinCVS的次數多了,很快就會熟悉它了。
    2. 這篇文檔只是窺探了CVS的一點皮毛而已,CVS當中還有很多高級的用法,以及上百個命令,還有很多新鮮的管理源代碼的方法,比如:tag,branch等模式;因此,熱烈歡迎大家積極探索,不斷共享,不斷進步。。。。。。。。
    3. 另外,cvs.html這個幫助,里面的信息也很豐富,但是,很多地方寫得不夠清楚,需要不斷猜測和實踐才能知道怎么回事,本文的很多經驗都是看這個幫助,如此這般,采用這個笨辦法得到的。。。。。。
    4. 最后,祝愿看到此文的人,得到的幫助、提高等好處大于或等于浪費的時間、反而退步等壞處!!

    posted @ 2007-12-01 13:31 jadmin 閱讀(66) | 評論 (0)編輯 收藏
    僅列出標題
    共50頁: First 上一頁 19 20 21 22 23 24 25 26 27 下一頁 Last 
    主站蜘蛛池模板: 亚洲国产另类久久久精品黑人| 亚洲AV日韩AV天堂一区二区三区| eeuss影院www天堂免费| 亚洲乱色熟女一区二区三区丝袜| 无码少妇精品一区二区免费动态 | 最近中文字幕大全中文字幕免费| 亚洲 欧洲 日韩 综合在线| 免费大片黄手机在线观看| 在线免费观看h片| 亚洲国产综合在线| 亚洲国产精品自在拍在线播放| 13小箩利洗澡无码视频网站免费| 亚洲人成电影院在线观看| 亚洲色偷偷狠狠综合网| 蜜臀AV免费一区二区三区| 国产亚洲蜜芽精品久久| 亚洲黄色免费网站| 五月婷婷亚洲综合| **真实毛片免费观看| 久青草国产免费观看| 亚洲免费在线观看视频| 亚洲啪啪AV无码片| 日韩免费a级在线观看| 亚欧免费一级毛片| 国产免费人成视频尤勿视频| 亚洲五月综合缴情婷婷| 亚洲AV永久纯肉无码精品动漫 | 国产嫩草影院精品免费网址| 午夜免费福利小电影| 羞羞视频免费网站日本| 亚洲第一男人天堂| 久久精品国产亚洲AV无码麻豆| 午夜亚洲国产成人不卡在线| 免费a级毛片无码a∨蜜芽试看 | 中文字幕亚洲第一| 麻豆国产人免费人成免费视频| 69影院毛片免费观看视频在线 | 日本xxwwxxww在线视频免费| 精品久久8x国产免费观看| 最近免费字幕中文大全| 深夜特黄a级毛片免费播放|