チュートリアルを参考に、DBから読み込んで計算
前回作ったDBからデータを引っ張ってきて、計算させてみる。
ほぼチュートリアル(Transport9.java)が参考になりそう。接続させるところだけ修正して、動かしてみた。
他に参考になりそうなところでは、いったんMySQLからcsvファイルを作り、それを読み込むような手法もあった(databases:mysql [GAMS Interfaces Wiki])。
ということで下のコードはTransport9.javaほぼそのまま。
public class TransportFromDB {
Modelを組み立て実行する
public void runJob() {
GAMSWorkspace ws = new GAMSWorkspace();
GAMSJob tp = ws.addJobFromFile("/PATH/TO/YOUR/Model.gms");
GAMSDatabase gdb;
try {
gdb = readDataFromDB(ws);
GAMSOptions opt = ws.addOptions();
opt.defines("gdxincname", gdb.getName());
opt.setAllModelTypes("xpress");
tp.run(opt, gdb);
for (GAMSVariableRecord rec : tp.OutDB().getVariable("x")) {
System.out.println("x(" + rec.getKeys()[0] + ","
+ rec.getKeys()[1] +
"): level=" + rec.getLevel() +
" marginal=" + rec.getMarginal());
}
} catch (SQLException ex) {
Logger.getLogger(TransportFromDB.class.getName()).log(Level.SEVERE, null, ex);
}
}
MySQLからデータを読み込む
@param ws
@return
@throws SQLException
private GAMSDatabase readDataFromDB(GAMSWorkspace ws) throws SQLException {
ResourceBundle rb = ResourceBundle.getBundle("mysql");
GAMSDatabase gdb = ws.addDatabase();
Connection conn = DriverManager.getConnection(
rb.getString("DB_URL"),
rb.getString("DB_USER"),
rb.getString("DB_PASSWORD")
);
readSet(conn, gdb, "SELECT Plant FROM Plant", "i", 1, "canning plants");
readSet(conn, gdb, "SELECT Market FROM Market", "j", 1, "markets");
readParameter(conn, gdb, "SELECT Plant, Capacity FROM Plant", "a", 1, "capacity of plant i in cases");
readParameter(conn, gdb, "SELECT Market, Demand FROM Market", "b", 1, "demand at market j in cases");
readParameter(conn, gdb, "SELECT Plant,Market,Distance FROM Distance", "d", 2, "distance in thousands of miles");
return gdb;
}
Set項目をDBから取得してGAMSDatabaseに設定する
@param conn
@param gdb
@param query
@param setName
@param setDimension
@param setDescription
@throws SQLException
private void readSet(Connection conn, GAMSDatabase gdb, String query, String setName, int setDimension, String setDescription) throws SQLException {
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(query);
ResultSetMetaData meta = rs.getMetaData();
if (meta.getColumnCount() != setDimension) {
System.err.println("ERROR!");
conn.close();
System.exit(-1);
}
GAMSSet set = gdb.addSet(setName, setDimension, setDescription);
String[] keys = new String[setDimension];
while (rs.next()) {
for (int i=0; i<setDimension; i++) {
keys[i] = rs.getString(i+1);
}
set.addRecord(keys);
}
st.close();
}
Parameter項目をDBから取得してGAMSDabaseに設定する
@param conn
@param gdb
@param query
@param paramName
@param paramDim
@param paramDesciption
@throws SQLException
private void readParameter(Connection conn, GAMSDatabase gdb, String query, String paramName, int paramDim, String paramDesciption) throws SQLException {
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(query);
ResultSetMetaData meta = rs.getMetaData();
int numberOfColumns = meta.getColumnCount();
if (numberOfColumns != (paramDim + 1)) {
System.err.println("ERROR!");
conn.close();
System.exit(-1);
}
GAMSParameter parameter = gdb.addParameter(paramName, paramDim, paramDesciption);
String[] keys = new String[paramDim];
while(rs.next()) {
for(int i=0; i<paramDim; i++) {
keys[i] = rs.getString(i+1);
}
parameter.addRecord(keys).setValue(Double.valueOf(rs.getString(numberOfColumns)));
}
st.close();
}
}
コードの中で読んでいるmysql.propertiesは下記で、src/main/resources
に置いておく。
# DBのプロパティ
DB_URL=jdbc:mysql://localhost:3306/transport
DB_USER=work
DB_PASSWORD=(workに対して設定したもの)
以前書いた最適化計算をGAMSで行う - GIGでは、Netbeans上から動作を確認したが、Netbeans8に上げてからか、システムの環境変数を拾ってくれない。
どこかで調べなくては。
結局コマンドライン上から動かして見たが、次に「おや?」と思ったのが、結果が以前の同じモデルの計算と異なる(ダメじゃん...)。
*.lstファイルを比較してみたけど、展開した式は同じ...。これも調べなくては。
モデルをひっぺがす
上記の場合、当然SQL分をべた書きしている箇所などは外から読ませほうがいい。
Set, Parameterなどデータの型や次元は、今データベースにいれていない。これらはモデルのメタデータというくくりで、実際のデータと分けた方が良いかもしれない。
例えば、以下のmodel.json
をModelを登録時に作成するなど(そういえば結果の格納は何にも考えていなかった...)
{
"Model" : {
"name" : "transport"
},
"Data" : {
"i" : {
"type" : "set",
"dim" : "1"
},
"j" : {
"type" : "set",
"dim" : "1"
},
"a" : {
"type" : "paramer",
"dim" : "1"
},
"b" : {
"type" : "paramer",
"dim" : "1"
},
"d" : {
"type" : "paramer",
"dim" : "2"
}
},
"Results" : ["x.l", "x.m"]
}
まだまだ考えがまとまっていない。。