Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.odysz.semantic.DASemantext Maven / Gradle / Ivy
package io.odysz.semantic;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io_odysz.FilenameUtils;
import io.odysz.common.Configs;
import io.odysz.common.Configs.keys;
import io.odysz.common.Radix32;
import io.odysz.common.Radix64;
import io.odysz.common.dbtype;
import io.odysz.module.rs.AnResultset;
import io.odysz.semantic.DATranscxt.SemanticsMap;
import io.odysz.semantic.DA.Connects;
import io.odysz.semantics.ISemantext;
import io.odysz.semantics.IUser;
import io.odysz.semantics.SemanticObject;
import io.odysz.semantics.meta.TableMeta;
import io.odysz.semantics.x.SemanticException;
import io.odysz.transact.sql.Delete;
import io.odysz.transact.sql.Insert;
import io.odysz.transact.sql.Query;
import io.odysz.transact.sql.Statement;
import io.odysz.transact.sql.Statement.IPostOptn;
import io.odysz.transact.sql.Statement.IPostSelectOptn;
import io.odysz.transact.sql.Transcxt;
import io.odysz.transact.sql.Update;
import io.odysz.transact.sql.parts.AbsPart;
import io.odysz.transact.sql.parts.condition.Condit;
import io.odysz.transact.x.TransException;
import static io.odysz.common.LangExt.isNull;
/**
* A basic semantic context for generating sql.
* Handling semantics defined in runtime-root/semantics.xml file.
*
* For example, {@link #pageSql(String, int, int)} is an example that must
* handled by context, but not interested by semantic.jserv. When composing SQL
* like select statement, if the results needing to be paged at server side,
* the paging sql statement is different for different DB. But semantic-transact
* don't care DB type or JDBC connection, so it's the context that will handling
* this.
*
* See the {@link Connects#pagingSql(dbtype, String, int, int)}.
*
* @author [email protected]
*/
public class DASemantext implements ISemantext {
/** FIXME Flat auto resolving pool is not enought! */
private SemanticObject autoVals;
private static Transcxt rawst;
/**Semantic Configurations */
protected SemanticsMap semants;
protected HashMap metas;
protected IUser usr;
protected String connId;
protected String basePath;
protected ArrayList onRowsOk;
protected LinkedHashMap onSelecteds;
protected LinkedHashMap onTableOk;
/**for generating sqlite auto seq */
protected static IUser sqliteDumyUser;
/**
* Initialize a context for semantics handling.
* This class handling semantics comes form path, usually an xml like test/res/semantics.xml.
* @param connId
* @param semanticsMap semantic configurations, usually load by {@link io.odysz.semantic.DATranscxt}.
* sample code:
* DATranscxt.initConfigs("inet", rootINF + "/semantics.xml");
* @param usr
* @param rtPath runtime root path, for docker layer, it's typically the volume folder.
* @throws SemanticException metas is null
* @throws SQLException
*/
protected DASemantext(String connId, SemanticsMap semanticsMap,
IUser usr, String rtPath) throws SemanticException, SQLException {
basePath = rtPath;
this.connId = connId;
semants = semanticsMap;
this.metas = Connects.getMeta(connId);
if (metas == null)
throw new SemanticException("DASemantext can not work without DB metas. connId: %s", connId);
if (rawst == null) {
rawst = new Transcxt(null);
}
this.usr = usr;
}
/**
* When inserting, process data row with configured semantics, like auto-pk, fk-ins, etc..
* @throws TransException
* @see io.odysz.semantics.ISemantext#onInsert(io.odysz.transact.sql.Insert, java.lang.String, java.util.List)
*/
@Override
public ISemantext onInsert(Insert insert, String tabl, List> rows)
throws TransException {
if (rows != null && semants != null)
// second round
for (ArrayList row : rows) {
Map cols = insert.getColumns();
DASemantics s = semants.get(tabl);
if (s == null)
continue;
s.onInsert(this, insert, row, cols, usr);
}
return this;
}
@Override
public ISemantext onUpdate(Update update, String tabl, ArrayList nvs)
throws TransException {
if (nvs != null && semants != null) {
Map cols = update.getColumns();
DASemantics s = semants.get(tabl);
if (s != null)
s.onUpdate(this, update, nvs, cols, usr);
}
return this;
}
@Override
public ISemantext onDelete(Delete delete, String tabl, Condit whereCondt)
throws TransException {
if (semants != null) {
DASemantics s = semants.get(tabl);
if (s != null)
s.onDelete(this, delete, whereCondt, usr);
}
return this;
}
@Override
public ISemantext onPost(Statement> stmt, String tabl, ArrayList row,
ArrayList sqls) throws TransException {
if (row != null && semants != null) {
Map cols = stmt.getColumns();
if (cols == null)
return this;
DASemantics s = semants.get(tabl);
if (s != null)
s.onPost(this, stmt, row, cols, usr, sqls);
}
return this;
}
@Override
public ISemantext insert(Insert insert, String tabl, IUser usr) throws SQLException {
return clone(this, usr);
}
@Override
public ISemantext update(Update update, String tabl, IUser usr) throws SQLException {
return clone(this, usr);
}
@Override
public dbtype dbtype() {
return Connects.driverType(connId);
}
@Override
public String connId() { return connId; }
@Override
public ISemantext connId(String conn) {
connId = conn;
return this;
}
@Override
public ISemantext clone(IUser usr) {
try {
return new DASemantext(connId, semants, usr, basePath);
} catch (SQLException | SemanticException e) {
e.printStackTrace();
return null; // meta is null? how could it be?
}
}
protected ISemantext clone(DASemantext srctx, IUser usr) {
try {
DASemantext newInst = new DASemantext(connId, srctx.semants, usr, basePath);
return newInst;
} catch (SemanticException | SQLException e) {
e.printStackTrace();
return null; // meta is null? how could it be?
}
}
@SuppressWarnings("unchecked")
@Override
public List resulvedVals(String tabl, String col) {
return autoVals != null && autoVals.has(tabl) ?
(List)((SemanticObject) autoVals.get(tabl)).get(col)
: null;
}
/**Get the resolved value in {@link #autoVals}
* a.k.a return value of {@link Update#doneOp(io.odysz.transact.sql.Statement.IPostOptn)}.
* @return {@link #autoVals}
* @see io.odysz.semantics.ISemantext#resulves()
*/
public SemanticObject resulves() {
return autoVals;
}
/*
public ISemantext reset() {
if (autoVals != null)
autoVals.clear();
return this;
}
*/
///////////////////////////////////////////////////////////////////////////
// auto ID
///////////////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
@Override
public String genId(String conn, String tabl, String col, String ... preval )
throws SQLException, TransException {
String newv = genIdPrefix(conn, tabl, col, null);
if (!isNull(preval))
newv = String.format("%s.%s", preval[0], newv);
if (autoVals == null)
autoVals = new SemanticObject();
SemanticObject tabl_ids = (SemanticObject) autoVals.get(tabl);
if (tabl_ids == null) {
tabl_ids = new SemanticObject();
autoVals.put(tabl, tabl_ids);
}
if (!tabl_ids.has(col))
tabl_ids.put(col, new ArrayList());
((ArrayList) tabl_ids.get(col)).add(newv);
return newv;
}
// @Override
// public ISemantext beginPostCommit(String parentName) {
// autoStacktop++;
// if (autostack != null && autostack.size() < autoStacktop)
// autostack.add(new SemanticObject());
// return this;
// }
//
// @Override
// public ISemantext endPostCommit() {
// autoStacktop--;
// return this;
// }
/**Generate new Id with the help of db function f_incSeq(varchar idName)
* Sql script for stored function:
* Mysql:
create FUNCTION f_incSeq2 (seqId varchar(100), prefix varchar(4)) RETURNS int(11)
begin
DECLARE seqName varchar(100);
DECLARE cnt INT DEFAULT 0;
if prefix = '' then set seqName = seqId;
else set seqName = concat(seqId, '.', prefix);
end if;
select count(seq) into cnt from ir_autoSeqs where sid = seqName;
if cnt = 0
then
insert into ir_autoSeqs(sid, seq, remarks) values (seqName, 0, now());
end if;
select seq into cnt from ir_autoSeqs where sid = seqName;
update ir_autoSeqs set seq = cnt + 1 where sid = seqName;
return cnt;
end;
* select f_incSeq2('%s.%s', '%s') newId
* Oracle:
CREATE OR REPLACE FUNCTION GZDX_YJPT.f_incSeq2(seqId in varchar, prefix in varchar) RETURN integer
IS
PRAGMA AUTONOMOUS_TRANSACTION;
seqName varchar(100);
cnt integer DEFAULT 0;
begin
if prefix = '' then seqName := seqId;
else seqName := concat(concat(seqId, '.'), prefix);
end if;
select count(seq) into cnt from ir_autoSeqs where sid = seqName;
if cnt = 0
then
insert into ir_autoSeqs(sid, seq, remarks) values (seqName, 0, to_char(sysdate, 'MM-DD-YYYY HH24:MI:SS'));
commit;
end if;
select seq into cnt from ir_autoSeqs where sid = seqName;
update ir_autoSeqs set seq = cnt + 1, remarks = to_char(sysdate, 'MM-DD-YYYY HH24:MI:SS') where sid = seqName;
commit;
return cnt;
end;
* select f_incSeq2('%s.%s', '%s') newId from dual
* auto ID for sqlite is handled by {@link #genSqliteId(String, String, String)} - needing table initialization.
* @param conn connection id
* @param target target table
* @param idField table id column (no multi-column id supported)
* @param subCate
* @return new Id (shortened in radix 64 by {@link Radix64})
* @throws SQLException
* @throws TransException
*/
protected static String genIdPrefix(String conn, String target, String idField, String subCate) throws SQLException, TransException {
// String connId = "";
dbtype dt = Connects.driverType(null);
if (dt == dbtype.sqlite)
return genSqliteId(conn, target, idField);
if (subCate == null) subCate = "";
String sql;
if (dt == dbtype.oracle)
sql = String.format("select \"oz_fIncSeq\"('%s.%s', '%s') newId from dual", target, idField, subCate);
else // mysql, etc.
sql = String.format("select oz_fIncSeq('%s.%s', '%s') newId", target, idField, subCate);
AnResultset rs = null;
// v1.3.0: user sys default conn to generate log-id
rs = Connects.select(Connects.defltConn(), sql);
if (rs.getRowCount() <= 0)
throw new TransException("Can't find auot seq of %1$s.\n" +
"For performantc reason, DASemantext assumes a record in oz_autoseq.seq (id='%1$s.%2$s') exists.\n" +
"Maybe you would check wheather oz_autoseq.seq and table %2$s are existing?",
idField, target);
rs.beforeFirst().next();
long newInt = rs.getLong("newId");
if (subCate == null || subCate.equals(""))
return radix64_32(newInt);
else
return String.format("%1$s_%2$6s", subCate,
radix64_32(newInt));
}
public static int file_sys = 0;
/**
* Try generate a radix 64 string of v.
* String length is controlled by connfigs.xml/k=db-len, overriding default 8 for windows, or 6 for others.
* If configs.xml/k=filesys is "windows", then generate a radix 32 string.
*
* @param v
* @return radix 64/32
*/
public static String radix64_32(long v) {
if (file_sys == 0)
file_sys = "windows".equals(Configs.getCfg(keys.fileSys)) ? 1 : 2;
if (file_sys == 2)
return Radix64.toString(v, Configs.getInt(keys.idLen, 6));
else
return Radix32.toString(v, Configs.getInt(keys.idLen, 8));
}
/**Generate auto id in sqlite.
* All auto ids are recorded in oz_autoseq table.
* See {@link DASemantextTest} for how to initialize oz_autoseq.
* @param conn
* @param target
* @param idF
* @return new Id
* @throws SQLException
* @throws TransException
*/
static String genSqliteId(String conn, String target, String idF) throws SQLException, TransException {
Lock lock;
lock = getAutoseqLock(conn, target);
// 1. update ir_autoseq (seq) set seq = seq + 1 where sid = tabl.idf
// 2. select seq from ir_autoseq where sid = tabl.id
ArrayList sqls = new ArrayList();
sqls.add(String.format("update oz_autoseq set seq = seq + 1 where sid = '%s.%s'",
target, idF));
AnResultset rs = null;
Query q = rawst.select("oz_autoseq").col("seq")
.where("=", "sid", String.format("'%s.%s'", target, idF));
// dumy user for update oz_autoseq
if (sqliteDumyUser == null)
sqliteDumyUser = new IUser() {
@Override public TableMeta meta(String ... connId) { return null; }
@Override public String uid() { return "sqlite-dumy"; }
@Override public IUser logAct(String funcName, String funcId) { return null; }
@Override public IUser notify(Object note) throws TransException { return this; }
@Override public List notifies() { return null; }
@Override public long touchedMs() { return 0; }
@Override public IUser sessionKey(String ssId) { return this; }
@Override public String sessionKey() { return null; } };
// each table has a lock.
// lock to prevent concurrency.
lock.lock();
try {
// for efficiency
Connects.commit(conn, sqliteDumyUser, sqls, Connects.flag_nothing);
rs = Connects.select(conn, q.sql(null), Connects.flag_nothing);
} finally { lock.unlock();}
if (rs.getRowCount() <= 0)
throw new TransException("Can't find auot seq of %1$s.\n" +
"For performance reason and difficulty of implementing sqlite stored process, DASemantext assumes a record in oz_autoseq.seq (id='%1$s.%2$s') exists.\n" +
"May be you would check where oz_autoseq.seq and table %2$s are existing?",
idF, target);
rs.beforeFirst().next();
return radix64_32(rs.getLong("seq"));
}
static Lock lockOflocks = new ReentrantLock();
/** [conn-id, [table, lock]]] */
private static HashMap> locks;
private static Lock getAutoseqLock(String conn, String tabl) {
lockOflocks.lock();
Lock l = null;
try {
if (locks == null)
locks = new HashMap>();
if (!locks.containsKey(conn))
locks.put(conn, new HashMap());
if (!locks.get(conn).containsKey(tabl))
locks.get(conn).put(tabl, new ReentrantLock());
l = locks.get(conn).get(tabl);
} finally { lockOflocks.unlock(); }
return l;
}
public String totalSql(String rawSql) throws TransException {
return DASemantext.totalSql(Connects.driverType(connId()), rawSql);
}
public String pageSql(String rawSql, int page, int size) throws TransException {
return Connects.pagingSql(connId, rawSql, page, size);
}
/**Wrap sql only for rows in a page, in stream mode.
* @param dt
* @param sql
* @param pageIx
* @param pgSize
* @return pagination wrapped sql
* @throws TransException
* @deprecated replaced by {@link Connects#pagingSql(dbtype, String, int, int)}
*/
public static String pagingSql(dbtype dt, String sql, int pageIx, int pgSize) throws TransException {
return Connects.pagingSql(dt, sql, pageIx, pgSize);
}
/**
* @param dt
* @param sql
* @return SQL: select count(*) as total from [sql]
* @throws TransException
*/
public static String totalSql(dbtype dt, String sql) throws TransException {
return Stream.of("select count(*) as total from (", sql)
.collect(Collectors.joining("", "", ") s_jt"));
}
/** @deprecated */
public void clear() {
autoVals = null;
if (onTableOk != null)
onTableOk.clear();
if (onRowsOk != null)
onRowsOk.clear();
if (onSelecteds != null)
onSelecteds.clear();
}
@Override
public TableMeta tablType(String tabl) {
return metas.get(tabl);
}
@Override
public String relativpath(String... sub) throws TransException {
return FilenameUtils.concat(".", sub);
}
@Override
public String containerRoot() { return basePath; }
@Override
public void onCommitted(ISemantext ctx, String tabl) throws TransException, SQLException {
if (onRowsOk != null)
for (IPostOptn ok : onRowsOk)
// onOk handlers shoudn't using sqls, it's already committed
ok.onCommitOk(ctx, null);
if (onTableOk != null && onTableOk.containsKey(tabl))
onTableOk.get(tabl).onCommitOk(ctx, null);
}
@Override
public void addOnRowsCommitted(IPostOptn op) {
if (onRowsOk == null)
onRowsOk = new ArrayList();
onRowsOk.add(op);
}
@Override
public void addOnTableCommitted(String tabl, IPostOptn op) {
if (onTableOk == null)
onTableOk = new LinkedHashMap();
onTableOk.put(tabl, op);
}
@Override
public IPostOptn onTableCommittedHandler(String tabl) {
return onTableOk == null ? null : onTableOk.get(tabl);
}
@Override
public boolean hasOnSelectedHandler(String name) {
return onSelecteds != null && onSelecteds.containsKey(name);
}
@Override
public void onSelected(Object resultset) throws SQLException, TransException {
AnResultset rs = (AnResultset) resultset;
if (rs != null && onSelecteds != null && onSelecteds.size() > 0) {
rs.beforeFirst();
while (rs.next())
for (IPostSelectOptn op : onSelecteds.values())
op.onSelected(this, rs.getRowCells(), rs.getColnames());
rs.beforeFirst(); // makes caller less error prone
}
}
@Override
public void addOnSelectedHandler(String name, IPostSelectOptn op) {
if (onSelecteds == null)
onSelecteds = new LinkedHashMap();
onSelecteds.put(name, op);
}
@Override
public AbsPart composeVal(Object v, String tabl, String col) {
if (v instanceof AbsPart)
return (AbsPart) v;
TableMeta mt = tablType(tabl);
return Statement.composeVal(v, mt, col);
}
@Override
public TableMeta getTableMeta(String tbl) {
return metas.get(tbl);
}
}