2011/05/11

一些撰寫Java程式的注意事項

資料庫存取方式

大致上可分成下面6個步驟

  1. 將資料庫驅動程式載入JVM
  2. 建立Connection物件
  3. 撰寫SQL
  4. 建立PreparedStatement物件,設定參數
  5. 執行SQL,取得資料
  6. 關閉連線及資料庫連線

範例

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test {

/**
* @param args
*/
public static void main(String[] args) {
// 1.將資料庫驅動程式載入JVM
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

// 2.建立Connection物件
String url = "jdbc:oracle:thin:@10.100.2.30:1521:pstest";
String user = "test";
String password = "test";
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
// exit system if no connection object
}

// 3.撰寫SQL
String sql = "select * from users where userid = ?";
try {
// 4. 建立PreparedStatement,設定參數
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "A12345678");
// 5. 執行SQL取得資料
ResultSet rs = ps.executeQuery();
if (rs.next()) {
System.out.println(rs.getString("name"));
}
// 6. 關閉連線
rs.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}


幾點注意一下



  1. 一個 try…catch 只對應一個例外情況,或者會造成相同類型的例外才包裹在一起,例如當Class.forName在物件載入JVM時有可能會發生ClassNotFoundException,那麼單獨對此行程式進行catch即可。try…catch內如果有太多的程式,那當錯誤發生時很難看出錯誤的位置,也會失去原本try…catch的本意。

  2. 執行SQL的物件有StatementPreparedStatement,後者可防止SQL injection的情況,不過要撰寫的程式碼會比較多,兩者可以交替使用。若使用PreparedStatement,參數型態為字串時,不必在SQL內特別加上引號,用setString時就會自動補上。setXXXXX型態的index從 1 開始。

2011/01/04

Python SQLAlchemy Sample

簡單記錄一下SQLAlchemy的用法

db.py

#!C:\Python25\python.exe
#coding: utf-8

from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, sessionmaker

Base = declarative_base()

# Job
class Job(Base):
__tablename__ = 'Job'

id = Column(Integer, Sequence('id_seq'), primary_key=True)
jod_dd = Column(Date)
job_02 = Column(String(50))
job_03 = Column(String(50))
job_04 = Column(Text)
job_05 = Column(Integer)
job_06 = Column(String(2))
job_07 = Column(Text)
job_08 = Column(Date)
job_09 = Column(Date)
job_10 = Column(Date)
job_11 = Column(Text)

def __init__(self, User, Dept):
self.job_02 = User
self.job_03 = Dept

def __repr__(self):
return "Job<'%s', '%s'>" % (self.job_02, self.job_03)

# Comment
class Comment(Base):
__tablename__ = "Comment"

id = Column(Integer, Sequence('id_seq'), primary_key=True)
comment_f = Column(Integer)
comment_01 = Column(Text)
comment_dd = Column(Date)
comment_02 = Column(String(100))

def __init__(self):
pass

def __repr__(self):
pass

# see http://www.sqlalchemy.org/docs/07/dialects/sqlite.html
from sqlite3 import dbapi2 as sqlite
engine = create_engine('sqlite:///twcsms.db', module=sqlite, echo=True)
# created in memory
# engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)

test_db.py


#!C:\Python25\python.exe
#coding: utf-8

import unittest
import db

from sqlalchemy.orm import relation, sessionmaker

class TestDb(unittest.TestCase):

def test_InsertJob(self):
# create session
Session = sessionmaker(bind=db.engine)
created_record = db.Job("Sunset", "Orz")
session = Session()
session.add(created_record)
session.commit()

if __name__ == "__main__":
unittest.main()

執行結果


image

2010/10/15

程式的效能瓶頸

程式的效能瓶頸可從兩個地方來分析:

  1. 資料面
  2. 業務邏輯面

在資料面上,掌控資料儲存的資料庫,大多都經過嚴格的測試,所以暫時可以排除資料庫的部分。先想想應用程式和資料庫連結的Connection部份,是否有完善的管理方式,或者採用了Connection Pool的設計方式來管理Connection建立與釋放。設計良好的Connection Pool套件除了可以減少Connection的開關,並配合某些資料庫的特性進行最佳化。

再來是思考SQL Command的部分,因為這部分多是人為進行,一個爛掉的SQL Command會搞垮資料庫整體的效能一點也不意外,所以可以先從每個SQL Command的地方開始最佳化整體的資料存取。

另一個效能的瓶頸分析觀點,可從業務邏輯上來思考,這個頻繁使用的物件,是否只為了進行一個細微的動作就初始化所有的屬性?這樣是否可以繼續往下再切割,以達到功能最小化?是否可以對此物件的資料使用cache?或者透過Singleton Pattern的方式常駐在記憶體?(這樣要多考慮資料的即時性與同步性)。某些複雜的邏輯是否可使用thread的方式重構?

在AP Server上,一般來說小型的Tomcat Server已能應付大多數連線的情況(數十人同時存取上線等),很多效能的瓶頸大多發生在沒有最佳化的SQL,及物件太過龐大,或者邏輯判斷太多。重構是個好方法,但是要遵循某些步驟:

  1. 不要去更動原本物件的合約,可透過委託的方式進行
  2. 透過對AP Server的設定,可對每個方法加入時間計算(可找找是否有相關的套件)
  3. 可以先從TestCase開始寫起,使用TestCase去測試原本的邏輯,重構後再用新的物件去測試原本的TestCase
  4. 所有程式只註解不刪除
  5. 最好從source control系統中拉出branch進行

2010/10/14

2010/10/06

物件導向設計原則(1):The Open-Closed principle (OCP)

OCP原則是說,軟體實體應該開放擴充性,但要封閉修改需求。要達到這原則,便要善用物件導向的三大特性:封裝、多型和繼承,並配合介面的設計。

OCP是物件導向設計的主要精神,不過不可能所有的模組都滿足OCP原則,我們只能盡可能地降低未滿足OCP原則的模組數量,以達到最大程度的reusability和maintainability。

用一個範例程式來看OCP,考慮下列程式碼,這是一個購物車內計算總金額的method:

旅途的鈴聲

%E4%BD%9A%E5%90%8D-%E4%BA%BA%E5%9C%A8%E6%97%85%E9%80%94%E6%B4%92%E6%B3%AA%E6%97%B6[1]

幸福藏在舊有的事物裡

237c3223[1]

思念

潮包!?

某天在三民路看到,遠方樓頂上有四個口字型合起來的斗大招牌,以左到右上到下的順序寫:「區欠足包」。

我一開始想到的是這又是哪個潮牌的包包品牌,後來回神以後才熊熊想到,它寫的應該是....