3.4.1 Giải thuật chuyển từ CDM sang PDM
Việc chuyển đồi từ CDM sang PDM sẽ được xử lý trên các file xml. Cụ thể chúng ta từ mô hình CDM xuất ra file xml sau đó chuyển sang mô hình PDM ở dạng xml và import trở lại mô hình PDM. Sau đây sẽ trình bày giải thuật chuyển từ xml CDM sang xml PDM.
Trước tiên chúng ta sẽ đọc file CDM chuyển toàn bộ các <entity> thành <table> tương ứng. Document pdm = XMLParser.createDocument(); NodeList entitycounts = cdm.getElementsByTagName("entitycount"); int entitycount =Integer.parseInt(((Element)entitycounts.item(0)).getAttribute("value ")); NodeList relationcounts = cdm.getElementsByTagName("relationcount"); int relationcount =Integer.parseInt(((Element)relationcounts.item(0)).getAttribute("val ue"));
Element root = pdm.createElement("diagram"); root.setAttribute("type", "pdm"); pdm.appendChild(root);
//chuyển các entity -> table
NodeList entitynodes = cdm.getElementsByTagName("entity"); for(int i =0;i<entitynodes.getLength();i++)
{
Element entitynode = (Element)entitynodes.item(i); Element table =
(Element)createTable(entitynode,cdm,pdm).cloneNode(true); root.appendChild(table);}
Đến phần giải quyết các mối quan hệ thì giải thuật chuyển sẽ tuân theo qui tắt sau:
Dựa vào bảng trên chúng ta sẽ có 6 trường hợp với 6 giải thuật tương ứng để giải quyết vấn đề các mối quan hệ khi chuyển đổi từ CDM sang PDM.
Trước hết chúng ta sẽ mô tả một hàm con được sử dụng trong các giải thuật trên. Đó là hàm addMainKey(Element table, Element column,Document pdm). Hàm này cho phép khi chúng ta thêm một khóa chính vào bảng thì đồng thời thêm khóa ngoại vào các bảng tham chiếu, đặc biệt nếu mối liên hệ đó có phụ thuộc hàm thì khi khóa ngoại được thêm vào cũng là khóa chính và nó lại tiếp tục cập nhật cho các bảng tham chiếu đến bảng vừa mới được thêm đó.
addMainKey(Element table, Element column,Document pdm) có 3 tham số đầu vào, table là bảng mà cần thêm khóa chính, column là khóa chính, pdm là đối tượng xml cần thêm vào khóa chính. Các làm việc của hàm như sau:
Dòng 1 là điều kiện để đảm bảo column là một khóa chính. Từ dòng 5 tới dòng 15 tạo ra một tab fk giống với tab column. Dòng 16 lấy danh sách tab <relationship> trong table. Chúng ta table là <table> trong tài liệu pdm. Khi dòng 16 được gọi. phương thức sẽ trả về các <relationship> là con của <table> hay nói cách khác là chỉ trả về các relationship mà table đó là nơi nó tham chiếu đến. Nên dòng lặp tiếp theo, từ dòng 17 đến dòng 32 sẽ duyệt từng relationship, để lấy bảng đầu tiên của relationship và thêm khóa ngoại fk vào đó. Đoạn code ở dòng 26 đến dòng 29 xét điều kiện xem nếu mối quan hệ đó có phụ thuộc hàm thì gọi đệ quy hàm addMainKey để thêm fk như một khóa chính vào bảng đầu tiên đó và tiếp tục thêm khóa ngoại vào các bản tham chiếu đến bảng đầu tiên đó.
1) Giải thuật này được thực thi với điều kiện endHead có kiểu là 0..1, startHead tùy ý:
Dòng code thứ 1 giúp lấy các column của bảng cuối của mối quan hệ, hay nói cách khác là bảng được tham chiếu tới. Vòng lặp từ 2 đến 23 sẽ duyệt từng column, nếu column đó là khóa chính thì sẽ thêm cột đó vào bảng đầu của mối quan hệ (bảng tham chiếu) như một khóa ngoại, trong trường hợp nếu có phụ thuộc hàm sẽ thêm cột đó như một khóa chính, và tiếp tục thêm vào các bảng tham chiếu đến bảng tham chiếu đó bằng cách gọi hàm addMainkey đã đề cập trước đó. Chúng ta chú ý, dòng code đầu tiên là chúng ta lấy các cột của bảng cuối của mối quan hệ. Vì như đã mô tả trong bảng giải thuật ở trên, chúng ta sẽ lấy các cột là khóa chính ở endEntity thêm vào startEntity làm khóa chính và dòng code 19 thêm vào các khóa ngoại có thể null.
Giải thuật này giống giải thuật 1, chỉ khác ở dòng 19 thay vì cho phép khóa ngoại null thì trường hợp này không có phép khóa ngoại null. Và chú ý trường hợp khi có endHead và startHead đều có kiểu là 1..1 thì khi có phụ thuộc hàm ta sẽ kéo về phía có phụ thuộc hàm. Theo mặc định ban đầu ta kéo về phía startEntity nên ta thêm đoạn code sau để giao hoán startEntity và endEntity khi xảy ra trường hợp đó.
//có phụ thuộc hàm thì phải kéo về phụ thuộc hàm if(relationnode.getAttribute("dependent").equals("2")) {
Element extra = starttable; starttable = endtable; endtable = extra;
}
3) 4) Giải thuật này thực hiện khi endHead là 0..n hoặc 1..n và startHead là 0..1 hoặc 1..1. 2 trường hợp này được hợp lại thành một trường hợp trong cài đặt.
Dòng thứ 1 cho ta thấy đoạn code này đang duyệt các phần tử của column của stardEntity và chúng ta sẽ đưa vào endEntity. Dòng 12 đến dòng 23 cho thấy, nếu có phụ thuộc hàm thì thêm vào endEntity dưới dạng khóa chính, còn ngược lại thì dưới dạng khóa ngoại.
5) Trường hợp 5 là trường hợp khá phức tạp chúng ta phải tạo ra một table mới và xử lý các vấn đề vị trí cho nó, thêm khóa chính ở 2 bảng vào cho nó và lúc trước chỉ có một mối quan hệ bên CDM thành một mối quan hệ tham chiếu bên PDM và giờ ta cũng phải khởi tạo một mối quan hệ để bảng mới tham chiếu đến 2 bảng kia.
Từ dòng code thứ 1 ta thấy đã tạo ra một bảng mới. Dòng 3 và dòng 4 ta lấy tên của mối quan hệ làm tên bảng mới đó. Dòng 6 đến dòng 9 ta lấy trung bình hoành độ và tọa độ của 2 bảng để gán cho bảng mới. từ dòng 11 đến dòng 30 chúng ta duyệt hết các khóa chính của startentity add vào bảng mới. Và từ dòng 31 đến 41 ta lấy khóa chính của endEntity thêm vào cho bảng mới. Như các trường hợp khác dòng 44 đến dòng 57 là lấy cái relationship bên CDM chuyển qua PDM còn dòng 58 đến dòng 64 là ta đã tạo ra một relatinoship mới để thêm vào bảng mới.
Tới đây là ta đã hoàn tất việc cài đặt giải thuật chuyển từ CDM đến PDM. Giải thuật này được cài đặt trong phương thức static Document convert(Document cdm) của gói com.luong.util.Cdmtopdm.
Begin BeginBegin Begin Gán chuỗi chuoi_create = “ ”; Gán chuỗi chuoi_alter = “ ”; Gán chuỗi fktable = “ ”; Gán chuỗi chuoi_create = “ ”; Gán chuỗi chuoi_alter = “ ”; Gán chuỗi fktable = “ ”; Lấy phần tử root của Document;
Từ phần tử root duyệt qua các Element có code là “table” lưu vào listTable;
int i =0 ;
Lấy phần tử root của Document;
Từ phần tử root duyệt qua các Element có code là “table” lưu vào listTable;
int i =0 ;
i < listTable.getLength();
Gán table = tên table[i]; Add vào chuỗi chuoi_create += talbe;
chuoi_create += “ { ”;
duyệt qua các Element có code là “Column” lưu vào
listColumn;; int j = 0; Gán table = tên table[i]; Add vào chuỗi chuoi_create += talbe;
chuoi_create += “ { ”;
duyệt qua các Element có code là “Column” lưu vào
listColumn;; int j = 0;
False
True
j < listColumn.getLength();
Tại mỗi column[j] Gán column = tên column[j]; Add vào chuoi_create tên column, type column, length;
tại Column[j] lấy giá trị fktable gán vào fktable; Tại mỗi column[j] Gán column = tên column[j]; Add vào chuoi_create tên column, type column, length;
tại Column[j] lấy giá trị fktable gán vào fktable;
False True fktable = -1; False True A
Xuất ra chuỗi chuoi_create += “ } ”; chuoi_create += chuoi_alter;
List ds_khoa = KhoaChinh(table[i]); List ds_khoa = KhoaChinh(table[i]);
ds_khoa.size() > 0
True
Add vào chuoi_create += “primary key( ”; int m = 0;
Add vào chuoi_create += “primary key( ”; int m = 0; m < ds_khoa.size()-1 True False chuoi_create += “ds_khoa.get[m]”; chuoi_create += “, ”; chuoi_create += “ds_khoa.get[m]”; chuoi_create += “, ”; chuoi_create += “ds_khoa.get[ds_khoa.size()-1]”; chuoi_create += “)”; chuoi_create += “ds_khoa.get[ds_khoa.size()-1]”; chuoi_create += “)”; False
k < listTable1.getLength(); code.table2[k] == fktable;
Từ phần tử table2[i] duyệt qua các node có name relationship và lưu vào listRelation;
int n=0;
Từ phần tử table2[i] duyệt qua các node có name relationship và lưu vào listRelation;
int n=0;
chuoi_atler += “ Alter table ”; chuoi_alter += table2[k].code; chuoi_alter += “ add constraint”; List ds_khoa1 = Khoa_Chinh(table2[k]);
g = 0;
chuoi_atler += “ Alter table ”; chuoi_alter += table2[k].code; chuoi_alter += “ add constraint”; List ds_khoa1 = Khoa_Chinh(table2[k]);
g = 0; g < ds_khoa1.size()-1 chuoi_alter += “ds_khoa1.get[g].code”; chuoi_create += “, ”; chuoi_alter += “ds_khoa1.get[g].code”; chuoi_create += “, ”; chuoi_alter += “ds_khoa1.get[ds.khoa1.size()-1].code”; chuoi_alter += “ ) ” ; chuoi_alter += “ references ”; chuoi_alter += table2[k].code; chuoi_alter += “ ( ”; chuoi_alter += “ds_khoa1.get[ds.khoa1.size()-1].code”; chuoi_alter += “ ) ” ; chuoi_alter += “ references ”; chuoi_alter += table2[k].code; chuoi_alter += “ ( ”; g < ds_khoa1.size()-1 chuoi_alter += “ds_khoa1.get[g].code”; chuoi_create += “, ”; chuoi_alter += “ds_khoa1.get[g].code”; chuoi_create += “, ”; chuoi_alter += “ds_khoa1.get[ds.khoa1.size()-1].code”; chuoi_alter += “ ) ”;
chuoi_alter += “on delete restrict on update restrict ”;
chuoi_alter +=
“ds_khoa1.get[ds.khoa1.size()-1].code”; chuoi_alter += “ ) ”;
chuoi_alter += “on delete restrict on update restrict ”;
False A
fktable += fktable + “*”;
Từ phần tử root duyệt qua các Element có code là “table” gán vào listTable1;
int k=0; fktable += fktable + “*”;
Từ phần tử root duyệt qua các Element có code là “table” gán vào listTable1;
int k=0; n < listRelation.getLength(); relation = listRelation[n]; relation = listRelation[n]; relation.starttable == table2[k].id; True True False False
Phương thức coverMysql có đối số đầu vào là một Document, đầu ra là một chuỗi StringBuffer. Trong phương thức này ta thường xuyên xử lý cộng dồn vào một chuỗi một chuối mới. Do đó để thuận tiện và tăng tốc độ của chương trình đồng thời giảm bớt tài nguyên sử dụng thì thay vì ta dùng toán tử cộng(+) như bình thường ta dùng phương thức append chuỗi mới vào một chuỗi kiểu StringBuffer.
Bắt đầu giải thuật chúng ta tạo ra ba chuỗi: chuoi_create, chuoi_alter, fktable. Chuỗi chuoi_create dùng để lưu trữ đoạn lệnh tạo các table, tạo các column, tạo khóa chính cho bảng đó. Chuỗi chuoi_alter dùng để lưu trữ đoạn lệnh tạo mối ràng buộc giữa các bảng, các khóa ngoại và các mối quan hệ giữa chúng. Chuỗi fktble dùng để đánh dấu xác định khóa ngoại tham chiếu tới table đó đã tồn tại hay chưa. Nếu chưa tồn tại thì tiến hành tạo mối quan hệ giữa table hiện hành với bảng nằm trong fktable của column hiện tại. Nếu đã tồn tại fktable trong chuỗi fktable thì bỏ qua và chuyển sang column khác và cứ tiếp tục cho đến hết các column, hết các table.
Kế đến ta lấy phần tử root của document, sau đó ta duyệt qua tất cả các nút con của phần tử root bằng phương thức getElementsByTagName() với name là table và lưu vào listTable:
Element root = PDM.getDocumentElement();
NodeList listTable = root.getElementsByTagName("table");
Sau đó ta dùng vòng lặp duyệt hết các phần tử trong listTable, tại mỗi table thứ i chúng ta gán vào element table. Tại mỗi table thứ i ta add vào chuoi_create bằng phương thức append một chuỗi “create table ”, tiếp đó là tên bảng ta dùng phương thức getAttributeNode() để lây lên code của table, và sau đó là mở một dấu ngoặc nhọn “{”:
chuoi_create.append("create table ");
chuoi_create.append(table.getAttributeNode("code").toString());
Chúng ta duyệt qua các tất cả column của table thứ i và gán vào
listColumn, tại mỗi column thứ i ta gán vào element column. Mỗi column chúng
ta lấy lên tên column, kiểu dữ liệu của column, độ dài của kiểu dữ liệu. Và ta cũng xét tới giá trị của cnull, nếu cnull = 0 thì ta append vào chuoi_create “ not null”. Sau khi hoàn tất một column ta append vào chuoi_create một dấy phẩy(,).
if("0".equals(column.getAttribute("cnull"))) {
tt.append(" not null"); }
Tại một column ta có giá trị chuỗi fktable, mặc định nếu column không phải là khóa ngoại của table nào thì fktable = “-1”. Như đã nói ở phần trên chuỗi fktable dùng để xác định xem quan hệ sẽ tạo có tồn tại chưa. Vì thế ở đây ta tại mỗi column có fktable khác “-1” thì ta cộng dồn vào chuỗi fktable giá trị của fktable. Ta dùng phương thức indexOf() để kiếm tra xem giá trị sắp cộng vào đã có chưa, nếu chưa thì cộng vào chuỗi fktable và tạo mối quan hệ các bảng và append vào chuoi_alter, ngược lại chuyển qua column kế tiếp.
if(column.getAttribute("fktable").equals("-1")) { // không làm gì } else {
int index = fktable.indexOf(column.getAttribute("fktable")); if(index == -1)
{
fktable = fktable + column.getAttribute("fktable") + "*"; // thực hiện tạo mối quan hệ và append vào chuoi_alter }
}
Khi xét fktable chúng ta duyệt lại lần nữa các table bằng phương thức getElenentsByTagName(). Tại mỗi table thứ k ta gán vào element table2. Ta xét nếu table2 nào có giá trị của id bằng với giá trị của fktable ta dừng lại và xét table2 này. Từ node của table2 này ta lấy ra các element có tên relationship. Tại các relationship thứ n, chúng ta gán vào element relation. Ta xét mỗi ralation nếu giá trị của starttable bằng với id của table đang xét (element table). Ta thực hiện append vào chuoi_alter một chuỗi “Alter table ”, lấy code của talbe, và thực hiện tạo mối quan hệ gán tiếp vào chuỗi create_alter. Việc tạo mối quan hệ được thực hiện bằng đoạn mã sau:
if(relation.getAttribute("starttable").equals(table.getAttribute("id" ))) { alter.append("\n"); alter.append("alter table "); alter.append(table.getAttribute("code"));
alter.append(" add constraint "); alter.append(relation.getAttribute("name"));
alter.append(" foreign key "); alter.append(" (");
key1 = getKey(table2); for(int g=0; g<key1.size()-1; g++) { alter.append(key1.get(g).getAttribute("code")); alter.append(", "); } alter.append(key1.get(key1.size()-1).getAttribute("code")); alter.append(") "); alter.append("\n"); alter.append("\t"); alter.append(" references "); alter.append(table2.getAttribute("code")); alter.append(" ("); for(int g=0; g<key1.size()-1; g++) { alter.append(key1.get(g).getAttribute("code")); alter.append(", "); } alter.append(key1.get(key1.size()-1).getAttribute("code")); alter.append(") ");
alter.append("on delete restrict on update restrict;"); }
Sau khi hoàn thành viêc tạo các column chúng ta thực hiện tạo khóa chính cho table thứ i đang xét. Đồng thời gán lại chuỗi fktable bằng chuỗi rỗng để thực hiện cho các table tiếp theo.
fktable = "";
List<Element> key = new ArrayList<Element>(); key = getKey(table); if(key.size() > 0 ) { tt.append("\n"); tt.append("primary key ("); for(int m=0; m<key.size()-1; m++) { tt.append(key.get(m).getAttribute("code")); tt.append(", "); } tt.append(key.get(key.size()-1).getAttribute("code")); tt.append(") "); } tt.append("\n");
tt.append("); ");
Sau khi hoàn thành tạo bảng thứ i chúng ta đóng câu lệnh create_table lại bằng dấu ngoặc đóng “}”.
Cuối chương trình sau khi đã duyệt qua tất cả các bảng ta append chuoi_alter vào chuoi_create và xuất ra kết quả.
Trong phương thức coverMySql() ta có sử dụng một phương thức getKey() với đối số đưa vào là một element table. Đầu ra của phương thức này là danh sách các element column là khóa chính của table đó.
private static List<Element> getKey(Element entity) {
List<Element> ls = new ArrayList<Element>();
final NodeList list = entity.getElementsByTagName("column"); for(int i=0; i<list.getLength(); i++)
{
final Element node = (Element)list.item(i); if(node.getAttribute("mainkey").equals("1")) { ls.add(node); } } return ls; }
Phương thức getKey() này trả về một danh sách kiểu mảng các giá trị element có mk bằng “1”. Khi cần dùng đến các giá trị trả về của phương thức ta chỉ việc chạy vòng lặp để lấy toàn bộ các giá trị mà phương thức này trả về.
3.5 XÁC THỰC VÀ CHỨNG THỰC KHI TRUY CẬP GOOGLE API
Kỹ thuật này đã được đề cập đến trong cơ sở lý thuyết và phần này chúng ta sẽ tìm hiểu cách ứng dụng nó vào đề tài. Chúng ta hình dùng cách nó hoạt động, đầu tiên người dùng click vào button google, chương trình sẽ chuyển người dùng đến trang web đăng nhập của Google, sau khi đăng nhập thành công chương trình sẽ set session cho người dùng với thuộc tính username là địa chỉ mail của người dùng. Giờ chúng ta sẽ bắt đầu:
Đầu tiên chúng ta tạo ra servlet LoginGoogleServlet trong gói com.luong.server. Khi người dùng click vào button Google thì yêu cầu sẽ được gửi đến serlvet này.
Chúng ta sử dụng đối tượng userService để một url login. Đối tượng userService được tạo từ UserServiceFactory. Để tạo được url login ta gọi phương thức userService.createLoginURL(callbackURL) trong đó callbackURL là redirecturl mà ta đã nói tới ở phần cơ sở lý thuyết. callbackURL là nơi mà response của google sẽ gửi đến. Trong trường hợp này callbackURL có giá trị là servlet LoginGoogleCallbackServlet. Dòng code sau trong servlet sẽ giúp ta lấy được địa chỉ email của người dùng.
Principal googleUser = request.getUserPrincipal();
Nếu googleUser null tức người dùng đã đăng nhập thất bại, ngược lại googleUser.getName() sẽ trả về địa chỉ email người dùng. Sau đó ta tìm xem trong datastore đã có user này chưa nếu chưa thì thêm vào và set session cho người dùng này.
3.6 KẾT QUẢ
Sau khi ứng dụng các kỹ thuật vừa mô tả vào đề tài, chúng ta đã phát triển thành công một chương trình đầy đủ các chức năng: vẽ sơ đồ CDM, vẽ sơ đồ PDM, chuyển sơ đồ CDM thành PDM và từ PDM thành code Mysql, lưu trữ sơ đồ, đăng nhập với tài khoản người dùng Gmail.
Tuy nhiên chương trình demo cũng chỉ mang tính chất nghiên cứu, nó không được xây dựng để đưa vào sử dụng. Đối với sơ đồ CDM vẫn còn thiếu các chức năng như thừa kế, mối quan hệ nó với chính nó. Chương trình cho phép lưu sơ đồ nhưng chưa có chức năng sửa thông tin hay xóa sơ đồ. Thiết kế cơ sở dữ