假设某交易员使用分布式系统在终端客户机录入一笔交易信息,然后系统经互联网将交易信息传入远程应用服务器,应用服务器处理相关业务逻辑,将数据变更递交后台数据库,返回客户端执行结果。
在整个流程中,为了保证数据的完整性,我们应当首先在客户机终端录入时,进行初始校验操作。例如检查资金输入格式,可以在文本输入框注册键盘输入操作事件的监听,并实现相应事件处理代码:
//==========================Program Description=========================
//程序描述: 保证资金输入格式正确性的键盘事件处理方法
//=================@author luhao================@version 1.0=============
public void jTextField1_keyReleased(KeyEvent e) { //保证输入的资金的格式正确性
String text = jTextField1.getText(); //获取整个输入字符串
int textleng = text.length(); //获取字符串长度
int inchar = 0; //初始当前输入ASCII码为0的空输入
if (textleng != 0)
inchar = (int) text.charAt(textleng - 1); //获得当前输入的ASCII码
if (!((inchar >= 48 && inchar <= 57) || inchar == 46 || inchar == 0 || inchar == 8)) { //若输入非法字符,即除数字、小数点、空或退格外字符
jTextField1.setText(text.substring(0,textleng - 1).toString()); //清除非法字符
}
if (inchar == 46) { //若输入小数点
int fpos = text.indexOf('.'); //找到第一次出现小数点在字符串数组的位置,若找不到返-1值
int spos = text.indexOf('.', fpos+1);//找到第二次出现小数点在字符串数组的位置,若找不到返-1值
if (spos != -1) { //若出现第二个小数点
jTextField1.setText(text.substring(0,textleng - 1).toString()); //清除非法字符
}
}
}
远程应用服务中,为提供高效、可靠的数据库操作逻辑,可以运用预处理、批处理、事务处理等技术。特别在涉及交易时,往往涉及相关数据的变化,它们要么一起执行,要么都不执行,必须用事务来处理。例如,下图为WebLogic应用服务器、SQL2K后台数据库环境下,实现交易信息录入的简单功能类。
//==========================Program Description=========================
//程序描述: 实现数据库操作的类DbDAO,含事务的批、预处理的交易录入功能
//=================@author luhao================@version 1.0=============
import java.io.*;
import java.sql.*;
import java.util.Properties;
import javax.sql.*;
import javax.naming.*;
public class DbDAO //数据库事务操作实现类
{
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
public DbDAO(){}
private static Context getInitialContext() throws Exception{ //初始化WebLogic Server JNDI上下文信息的静态方法
String url = "t3://localhost:7001"; //指定应用服务器的主机和端口,7001为WebLogic默认端口
String user="weblogic"; //指定WebLogic登陆用户名
String password="weblogic"; //指定WebLogic登陆密码
Properties properties=null;
try{
properties=new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY , "weblogic.jndi.WLInitialContextFactory"); //指定服务提供者
properties.put(Context.PROVIDER_URL,url); //指定使用目录服务的主机和端口
if(user!=null){
properties.put(Context.SECURITY_PRINCIPAL,user);
properties.put(Context.SECURITY_CREDENTIALS,password==null?"":password);
}
return new InitialContext(properties);
}catch(Exception e){
throw e;
}
}
public boolean connect() //连接已架设好的SQL2Ksp3数据库后台的WebLogic应用服务器方法
{
boolean isConnect = false;
DataSource ds = null;
Context ctx = null;
try{
ctx = getInitialContext(); //获得WebLogic Server JNDI初始化上下文信息,即获取目录上下文的引用
ds = (javax.sql.DataSource)ctx.lookup("MyDataSource"); //建立数据源对象,MyDataSource为架设应用服务器时的配置好的数据源
}catch (Exception E) {
System.out.println("Init Error:" + E);
}
try{
conn = ds.getConnection(); //建立连接
if(conn != null)
isConnect = true;
}catch(Exception e){
System.out.println("connect to database failed");
e.printStackTrace();
}
return isConnect;
}
public boolean add_deal(String deal_id,String manager_id,String buyer_id,String seller_id,float price,int number,Date date,TIME time) //添加交易记录
{
boolean op_success=false;
try {
conn.setAutoCommit(false); //关闭Connection的自动提交,实现事务处理
conn.setTransactionIsolation(conn.TRANSACTION_SERIALIZABLE); //设置最高事务级别
pstmt = conn.prepareStatement("insert into tab_deal(deal_id,manager_id,buyer_id,seller_id,price,number,date,time) values(?,?,?,?,?,?,?,?)"); //对使用频繁的Sql语句,如此处添加交易信息,采用预编译形式,获得更高执行效率
pstmt.setString(1,deal_id); //参数设置具体值
pstmt.setString(2,manager_id);
pstmt.setString(3,buyer_id);
pstmt.setString(4,seller_id);
pstmt.setFloat(5,price);
pstmt.setInt(6,number);
pstmt.setDate(7,date);
pstmt.setTime(8,time);
pstmt.addBatch(); //添加入批处理流
pstmt.close(); //执行下一sql语句前,上一次的PreparedStatemet须先close
pstmt = conn.prepareStatement("update tab_account set money=money-?,amount=amount+? where uid=’?’"); //买家帐户上减去金额,增加数量
pstmt.setFloat(1,price*number);
pstmt.setInt(2,number);
pstmt.setString(3,buyer_id);
pstmt.addBatch();
pstmt.close();
pstmt = conn.prepareStatement("update tab_account set money=money+?,amount=amount-? where uid=’?’"); //卖家帐户上增加金额,减去数量
pstmt.setFloat(1,price*number);
pstmt.setInt(2,number);
pstmt.setString(3,seller_id);
pstmt.addBatch();
pstmt.close();
int[] updateCounts=pstmt.executeBatch(); //执行批处理,减少系统开销
conn.commit(); //成功递交
op_success=true;
}catch(Exception e){
e.printStackTrace();
try{
conn.rollback(); //出错回滚
} catch (SQLException esgl) {
esgl.printStackTrace();
}
} finally {
if(conn != null) {
conn.setAutoCommit(true); //退出事务,恢复自动提交
}
return op_success;
}
public ResultSet select(String sql) //通用查询语句调用方法
{
try
{
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
}
catch(Exception e)
{
e.printStackTrace();
}
return rs;
}
public void close() //关闭数据库连接,释放资源
{
try{
if(rs != null)
rs.close();
rs = null;
if(stmt != null)
stmt.close();
stmt = null;
if(conn != null)
conn.close();
}catch(Exception e){
e.printStackTrace();
}finally{
conn = null;
}
}
}
实际应用服务器的数据连接,建议使用中间件服务器的池连接技术。池连接作为中间件服务器提供的一种数据访问技术,它在服务器启动时就会建立一定数量的池连接并一直维持一定数量的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的连接并将其标记为忙。如果没有空闲的连接,池驱动就新建一定数量的连接。当使用连接池的调用完成后,池驱动又将此连接标记为空闲,但不断开与数据库的连接,这样其它调用又可使用该连接,能够既节省资源又提高性能。
此外,些情况下,如交易员更换自身ID号而引起大量相关数据的变化,我们可以通过在交易员信息表上安装触发器,当事件发生时,自动触发其它表中相关信息的变化,保证数据的一致性。例如:
//==========================Trigger Description==========================
//触发器描述: SQL2K下实现用户表的用户ID修改,触发所有相关表内该用户ID的修改
//=================@author luhao================@version 1.0=============
CREATE TRIGGER [personid_update] ON [personInfo]
for UPDATE
AS
if UPDATE(person_id)
declare @old_id varchar(10)
declare @new_id varchar(10)
select @old_id=person_id from DELETED
select @new_id=person_id from INSERTED
update [manager_inf] set ID=@new_id where ID=@old_id
update [user_right] set user_id=@new_id where user_id=@old_id
……
通常一些全球化的金融系统应用分布广范,跨越了不同时区、语言,程序必须向处在不同时区或不同国家的用户显示时间和语言。此外,对交易的处理、数据的管理等也需要在系统内部进行不同时间、语言间的转换或统一。因此,这些用来处理
Multi-locations问题的系统功能显得十分重要,下面举例假设英、法两地系统使用时须涉及的转化方法实现:
//==========================Program Description==========================
//程序描述:日语与法语的示范转换程序LagChange.java
//=================@author luhao================@version 1.0==============
import java.util.Locale;
public class LagChange {
public static void main(String[] args) {
. Locale localeJP = new Locale("ja", "JP"); //用ISO标准化的标识建立一个地点为日本的地点对象,其中JP表示国家,ja为语言
System.out.println("Display Name: " + localeJP.getDisplayName()); //显示对象名,中文化Java开发环境执行显示“Display Name:日文(日本)”
System.out.println("Country: " + localeJP.getCountry()); //显示国家属性,执行显示“Country:JP”
System.out.println("Language: " + localeJP.getLanguage());//显示语言属性,执行显示“Language:ja”
Locale localeFr = new Locale("fr", "Fr"); //建立一个地点为法国的地点对象,fr为语言
System.out.println("Display Name: " + localeFr.getDisplayName()); //显示“Display Name:法文(法国)”
System.out.println("Country: " + localeFr.getCountry()); //执行显示“Country:Fr”
System.out.println("Language: " + localeFr.getLanguage());//执行显示“Country:fr”
System.out.println("ja Display Name in French: " + localeEN.getDisplayName(localeFr)); //将“日文(日本)”一词用法语来显示,执行显示“ja Display Name in French: japonais (Japon)”
}
}
//==========================Program Description==========================
//程序描述:示范在美国显示本地时间与时区转换后法国当地时间的程序TimeChange.java
//=================@author luhao================@version 1.0==============
import java.util.TimeZone;
import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
public class TimeChange {
public static void main(String[] args) {
Locale localeAmerica = Locale.US; //美国本地地点对象
Locale localeFrance = Locale.FRANCE; //法国地点对象
TimeZone timeZoneAmerica = TimeZone.getDefault(); //获得默认时区对象(假设本机默认为美国)
TimeZone timeZoneFrance = TimeZone.getTimeZone("Europe/Paris"); //获得法国时区对象
DateFormat dateFormatterAmerica = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,localeAmerica); //美国时间格式对象
Date altDate = new Date();
System.out.println("Display for English:");
System.out.println(timeZoneAmerica.getDisplayName(localeAmerica)); //用英语申明当前为美国标准时间
dateFormatterAmerica.setTimeZone(timeZoneAmerica); //设置美国时区
System.out.println(dateFormatterAmerica.format(altDate)); // 用英语显示美国本地时间
dateFormatterAmerica.setTimeZone(timeZoneFrance); //设置改为法国时区
System.out.println(timeZoneFrance.getDisplayName(localeAmerica)); // 用英语申明显示法国时区
System.out.println(dateFormatterAmerica.format(altDate)); // 用英语显示法国当前时间
}
}