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.
The default table semantics plugin used by semantic-DA.
*
* The {@link DASemantext} use this to manage semantics configuration for
* resolving data semantics.
*
* DASemantics is basically a semantics handler, {@link SemanticHandler} 's container,
* with subclass handlers handling different semantics (processing values).
*
*
What's DASemantics for?
*
* Well, the word semantics is a computer science term. The author don't want to
* redefine this word, but here is some explanation what semantic-DA with
* semantic-transact is trying to support.
*
*
* In a typical relational database based application, the main operation of
* data is CRUD. And the most often such data operation can be abstracted to
* some operation pattern, and they are always organized as a database
* transaction/batch operation described in SQL.
*
*
* Take "book-author" relation for example, the author's ID is also the parent
* referenced by book's author FK. If trying to delete an author in DB, there
* are 2 typical policies can be applied by the application. The first is delete
* all books by the author accordingly; the second is warning and denying the
* operation if some books are referencing the author. Both of this must/can
* been organized into a transact/batch operation, with the second transact as
* check-then-delete.
*
*
* In this case, you will find the FK relationship can be handled in a
* generalized operation, through parameterizing some variables like table name,
* child referencing column name and parent ID.
*
*
* Take the {@link DASemantics.smtype#parentChildrenOnDel} for example, it's
* automatically support "deleting all children when deleting parent" semantics.
* What the user (application developer) need to do is configure a semantics
* item then delete the parent directly.
*
*
* Now you (a developer) will definitely understand what's the
* "parentChildrenOnDel" for. Semantic-DA abstract and hide these patterns,
* wrapped them automatically into a transaction. That's what semantic- DA want
* to do.
*
*
How to Use
*
* To use this function:
*
*
* 1. Configure the "semantics.xml". See example in
* test/resources/semantics.xml.
* 2. Set the configured semantics as context of
* {@link io.odysz.transact.sql.Statement}. See example in
* {@link io.odysz.semantic.DASemantextTest}. Then use Statement's subclass's
* commit() method to generate SQLs
*
*
Is this Enough?
*
* The 9 or 10 types of semantics defined in {@link DASemantics.smtype} is
* enough for some enterprise projects. It depends on how abstract the semantics
* we want to support. But it seems enough for us, at least now.
*
*
* Another consideration is that semantic-DA never take supporting all semantics
* logic as it's goal. It's only trying to release burden of daily repeated
* tasks. Fortunately, such tasks' logic is simple, and the burden is heavy. Let
* semantic-* handle these simple logic, that's semantic-* designed for. If the
* semantics is complex, use anything you are familiar with. But in this case
* semantic-* are still useful to do this tasks, if users are familiar with the
* lower level API.
*
*
* Before doing that, check the semantics-cheapflow workflow engine first, which
* is based on semantics-*, and can handle typical - not very cheap if by our
* define - logics all necessary for enterprise applications. It's a good
* example illustrating that if the semantics is designed carefully, those
* semantics supported by this pattern is enough.
*
*
* But it do needs the application developers follow some design conventions. If
* you need you own semantics implementation, implement the interface
* {@link ISemantext}, or simply initialize
* {@link io.odysz.transact.sql.Transcxt} with null semantics, which will
* disable semantic supporting. In that way, it's working as a structured sql
* composing API.
*
*
Automatically fill merged child fk when inserting.
* Only referenced auto pk can be resolved.
*
About Merged Child Table:
* Take the attachements table for external file's information for example,
* the a_attaShExtFileches(See
*
* sqlite test DB) has a field, named 'busiId', referencing multiple parent table.
* The parent table is distinguished with filed busiTbl.
*
args: 0: parent Id field, 1: sibling/sort field (optional), 2: fullpath field, 3: sort size (optional, default 2)
* where sort size is the digital length for formatting fullpath string.
args: [pc-define, ...], where pc-define is a space sperated strings:
pc-define[0] name or child referencing column, e.g. domainId for a_domain.domainId
pc-define[1] child table, e.g. a_orgs
pc-define[2] child fk (or condition columnr), e.g. orgType
Example: domainId a_orgs orgType, ...
When deleting a_domain, the sql of the results shall be:
delete from a_orgs where orgType in (select domainId from a_domain where domainId = '000001')
where the 'where clause' in select clause is composed from condition of the delete request's where condition.
*
args: [pc-define, ...], where pc-define is a space sperated strings:
pc-define[0] name or child referencing column (a_domain.domainId's value will be used)
pc-define[1] child table
pc-define[2] child fk (or condition column)
pc-define[3] child cate (e.g. table name)
Example: domainId a_orgs orgType, ...
The sql of the results shall be:
delete from a_orgs where orgType in (select domainId from a_domain where domainId = '000001')
where the 'where clause' in select clause is composed from condition of the delete request's where condition.
*
*
* Handler: {@link DASemantics.ShPCDelByCate}
*/
parentChildrenOnDelByCate,
/**
* "d-e" | "de-encrypt" | "dencrypt":
* decrypt then encrypt (target col cannot be pk or anything other semantics
* will updated
* Handler: {@link DASemantics.ShDencrypt}
*/
dencrypt,
/**
* "d-e.r" | "de-encrypt.r" | "dencrypt.r":
* @deprecated to be implemented: new semantics handler: de-encrypt field on reading (in case client
* requiring read plain password etc. by accident.
*/
dencryptOnRead,
/**
* xml/smtc = "o-t" | "oper-time" | "optime"
* Finger printing session user's db updating - record operator / oper-time
* Handler: {@link DASemantics.ShOperTime}
*/
opTime,
/**
* "ck-cnt-del" | "check-count-del" | "checksqlcountondel":
* check is this record a referee of children records - results from
* sql.select(count, description-args ...). The record(s) can't been deleted if
* referenced;
*
*
* [0] name or child referencing column (a_domain.domainId's value will be used)
[1] child table
[2] child pk (or condition column)
Example: domainId a_orgs orgType, ...
The sql of the results shall be:
select count(orgType) from a_orgs where orgType in (select domainId from a_domain where domainId = '000001')
where the 'where clause' in select clause is composed from condition of the delete request's where condition.
*
*
* where args are column name of parent table.
*
* Handler: {@link DASemantics.ShChkCntDel}
*/
checkSqlCountOnDel,
/**
* "ck-cnt-ins" | "check-count-ins" | "checksqlcountoninsert":
* Check is this record count when inserting - results from
* sql.select(count-sql, description-args ...). The record(s) can't been
* inserted if count > 0;
*
* args: [0] arg1, [1] arg2, ..., [len -1] count-sql with "%s" formatter
* where args are column name of parent table.
*
* semantics:
* post fk wire back - parent has an fk to child (only one
* child makes sense, like making cross refs)
*
*
* Note:
* This semantics works only when previously resolved auto key exists; if the
* value doesn't exist, will be ignored.
* The former is the case of inserting new child, and parent refer to it;
* the later is the case of updating a child, the parent already has it's pk,
* nothing should be done.
*
*
r
* Further Discussion:
* As cross reference is not a good ideal, this semantics sometimes leads to
* trouble. Any suggestion or comments are welcome.
*
*
* args: 0 referencing col, 1 target table, 2 target pk(must be an auto
* key)
*
* Handler: {@link DASemantics.ShPostFk}
*/
postFk,
/**
* xml/smtc = "ef" | "xf" | "ext-file" | "e-f" | "x-f"
* @deprecated this is the same with {@link #extFilev2}
*/
extFile,
/**
* xml/smtc = "ef2.0" | "xf2.0" | "ext-file2.0" | "e-f2.0" | "x-f2.0"
*
Save and load a special field as file of file system.
* Can handle more subfolder (configered in xml as field name of data table).
*
The file content should be a Base 64 encoded block.
*
This semantics only used for handling small files.
* If the file is large, there are an example in Semantic.jserv which
* uses a block sequence for uploading files.
*
Tips
*
Multiple nodes writing into the same file path can causing the file locking exception.
*
args:
* 0: rec-id
* 1: uri, the Base64 content
* 2: subfolder
* 3: ...
*-1: file name
*
*
args
* 0: uploads,
* 1: uri - uri field,
* 2: sub-folder level 0,
* 3: sub-folder level 1,
* ... ,
*-1: client-name for saving readable file name
* At least one level of subfolder is recommended.
* @since 1.4.25
*/
extFilev2,
/**
* xml/smtc = "syn-change" | "s-c"
* Logging table changes for DB synchronizing.
*
*
* args
* 0: full qualified name of table meta
* 1: global identity fields, separated with space, e.g. "synoder clientpath"
* 2: [deprecated] fields to be cleared (optional with empty value), separated with space
*
* Example: <args>io.odysz.semantic.meta.SynodeMeta,crud,org synid,remarks</args>
*/
synChange
;
/**
* "cmp-col" | "compose-col" | "compse-column": compose a column from other
* columns;
* TODO
composingCol
*/
/**
* Convert string key to {@link smtype}.
*
* Note: we don't use enum.valueOf(), because of fault / fuzzy tolerate.
*
* @param type
* @return {@link smtype}
* @throws SemanticException
*/
public static smtype parse(String type) throws SemanticException {
if (type == null)
throw new SemanticException("semantics is null");
type = type.toLowerCase().trim();
if ("auto".equals(type) || "pk".equals(type) || "a-k".equals(type) || "autopk".equals(type))
return autoInc;
else if ("fk".equals(type) || "pkref".equals(type) || "fk-ins".equals(type))
return fkIns;
else if ("fk-ins-cate".equals(type) || "f-i-c".equals(type) || "fkc".equals(type))
return fkCateIns;
else if ("fp".equals(type) || "f-p".equals(type) || "fullpath".equals(type))
return fullpath;
else if ("dfltval".equals(type) || "d-v".equals(type) || "dv".equals(type))
return defltVal;
else if ("pc-del-all".equals(type) || "parent-child-del-all".equals(type)
|| "parentchildondel".equals(type))
return parentChildrenOnDel;
else if ("pc-del-tbl".equals(type) || "pc-del-by-tabl".equals(type)
|| "pc-tbl".equals(type))
return parentChildrenOnDelByCate;
else if ("d-e".equals(type) || "de-encrypt".equals(type) || "dencrypt".equals(type))
return dencrypt;
else if ("o-t".equals(type) || "oper-time".equals(type) || "optime".equals(type))
return opTime;
else if ("ck-cnt-del".equals(type) || "check-count-del".equals(type) || "checksqlcountondel".equals(type))
return checkSqlCountOnDel;
else if ("ck-cnt-ins".equals(type) || "check-count-ins".equals(type)
|| "checksqlcountoninsert".equals(type))
return checkSqlCountOnInsert;
else if ("p-f".equals(type) || "p-fk".equals(type) || "post-fk".equals(type))
return postFk;
else if ("ef2.0".equals(type) || "e-f2.0".equals(type) || "ext-file2.0".equals(type)
|| "xf2.0".equals(type) || "x-f2.0".equals(type))
return extFilev2;
else if ("s-c".equals(type) || "syn-change".equals(type))
return synChange;
else if ("s-up1".equals(type) || type.startsWith("stamp1"))
throw new SemanticException("Semantic type stamp1MoreThanRefee is deprecated.");
else if ("clob".equals(type) || "orclob".equals(type))
// return orclob;
throw new SemanticException("Since v1.4.12, orclob is no longer supported.");
else if ("ef".equals(type) || "e-f".equals(type) || "ext-file".equals(type)
|| "xf".equals(type) || "x-f".equals(type))
return extFile;
else
throw new SemanticException("semantics not known, type: " + type);
}
}
/**
* Static transact context for DB accessing without semantics support.
* Used to generate auto ID.
*/
protected Transcxt basicTsx;
/**
* Use this to replace metas from DB for semantics extension.
*
* @deprecated since 2.0.0, to have a meta be the type of
* {@link io.odysz.semantic.meta.SemanticTableMeta SemanticTableMeta}, configure
* the class name in semantics.xml/t[id=metas], instead of calling this method.
*
* @since 1.4.25
* @param tbl
* @param m
* @param connId
* @return replaced meta
* @throws TransException
* @throws SQLException
*/
static public TableMeta replaceMeta(String tbl, TableMeta m, String ... connId)
throws TransException, SQLException {
String conn = isNull(connId) ? Connects.defltConn() : connId[0];
TableMeta mdb = Connects.getMeta(conn, m.tbl);
if (mdb == null)
throw new TransException("Can't find table %s from DB connection %s.", m.tbl, conn);
Connects.setMeta(conn, m.clone(mdb));
return mdb;
}
///////////////////////////////// container class
///////////////////////////////// ///////////////////////////////
protected ArrayList handlers;
private String tabl;
private String pk;
/**
* Note: basicTx is already created per the connection, i. e. connect id is known.
* @param basicTx
* @param tabl
* @param recId
* @param verbose
*/
public DASemantics(Transcxt basicTx, String tabl, String recId, boolean ... verbose) {
this.tabl = tabl;
this.pk = recId;
basicTsx = basicTx;
this.verbose = isNull(verbose) ? false : verbose[0];
handlers = new ArrayList();
}
public DASemantics addHandler(SemanticHandler h) {
if (verbose)
h.logi();
handlers.add(h);
return this;
}
public void addHandler(smtype semantic, String tabl, String recId, String[] args)
throws Exception {
checkParas(tabl, pk, args);
if (isDuplicate(tabl, semantic))
return;
SemanticHandler handler = null;
handler = parseHandler(basicTsx, tabl, semantic, recId, args);
if (verbose)
handler.logi();
handlers.add(handler);
}
public SemanticHandler handler(smtype sm) {
if (handlers == null)
return null;
for (SemanticHandler h : handlers) {
if (h.is(sm))
return h;
}
return null;
}
public SemanticsMap createSMap(String conn) {
return new SemanticsMap(conn);
}
public SemanticHandler parseHandler(Transcxt trb, String tabl, smtype semantic, String recId, String[] args)
throws Exception {
if (smtype.fullpath == semantic)
return new ShFullpath(basicTsx, tabl, recId, args);
else if (smtype.autoInc == semantic)
return new ShAutoKPrefix(basicTsx, tabl, recId, args);
else if (smtype.fkIns == semantic)
return new ShFkOnIns(basicTsx, tabl, recId, args);
else if (smtype.fkCateIns == semantic)
return new ShFkInsCates(basicTsx, tabl, recId, args);
else if (smtype.parentChildrenOnDel == semantic)
return new ShPCDelAll(basicTsx, tabl, recId, args);
else if (smtype.parentChildrenOnDelByCate == semantic)
return new ShPCDelByCate(basicTsx, tabl, recId, args);
else if (smtype.defltVal == semantic)
return new ShDefltVal(basicTsx, tabl, recId, args);
else if (smtype.dencrypt == semantic)
return new ShDencrypt(basicTsx, tabl, recId, args);
// else if (smtype.orclob == semantic)
// addClob(tabl, recId, argss);
else if (smtype.opTime == semantic)
return new ShOperTime(basicTsx, tabl, recId, args);
else if (smtype.checkSqlCountOnDel == semantic)
return new ShChkCntDel(basicTsx, tabl, recId, args);
else if (smtype.checkSqlCountOnInsert == semantic)
return new ShChkPCInsert(basicTsx, tabl, recId, args);
else if (smtype.postFk == semantic)
return new ShPostFk(basicTsx, tabl, recId, args);
else if (smtype.extFile == semantic)
// throw new SemanticException("Since 1.5.0, smtype.extFile is replaced by extFilev2!");
return new ShExtFilev2(basicTsx, tabl, recId, args);
else if (smtype.extFilev2 == semantic)
return new ShExtFilev2(basicTsx, tabl, recId, args);
else
throw new SemanticException("Cannot load configured semantics of key: %s, with trans-builder: %s, on basic connection %s.",
semantic, trb.getClass().getName(), trb.basictx().connId());
}
/**
* Throw exception if args is null or target (table) not correct.
*
* @param tabl
* @param pk
* @param args
* @throws SemanticException
* sementic configuration not matching the target or lack of args.
*/
private void checkParas(String tabl, String pk, String[] args) throws SemanticException {
if (tabl == null || pk == null || args == null || args.length == 0)
throw new SemanticException(String.format("adding semantics with empty targets? %s %s %s", tabl, pk, args));
if (this.tabl != null && !this.tabl.equals(tabl))
throw new SemanticException(
String.format("adding semantics for different target? %s vs. %s", this.tabl, tabl));
if (this.pk != null && !this.pk.equals(pk))
throw new SemanticException(
String.format("adding semantics for target of diferent id field? %s vs. %s", this.pk, pk));
}
/**
* Check is the semantics duplicated?
*
* @param tabl
* @param newSmtcs
* @return false no duplicating, true duplicated
* @throws SemanticException
*/
private boolean isDuplicate(String tabl, smtype newSmtcs) throws SemanticException {
if (handlers == null)
return false;
for (SemanticHandler handler : handlers)
if (handler.sm == newSmtcs && newSmtcs != smtype.fkIns && newSmtcs != smtype.postFk) {
Utils.warn("Found duplicate semantics: %s %s\\n"
+ "Details: All semantics configuration is merged into 1 static copy. Each table in every connection can only have one instance of the same smtype.",
tabl, newSmtcs.name());
return true;
}
return false;
}
public boolean has(smtype sm) {
if (handlers != null)
for (SemanticHandler handler : handlers)
if (handler.sm == sm)
return true;
return false;
}
public void onInsert(ISemantext semantx, Insert statemt, ArrayList