dbo CRUD first iter
This commit is contained in:
@@ -13,6 +13,12 @@
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="build/classes/java/main" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="build-default"/>
|
||||
|
||||
Vendored
+4
-4
@@ -1,6 +1,9 @@
|
||||
.vscode/
|
||||
!.vscode/launch.json
|
||||
|
||||
build/
|
||||
dist/
|
||||
target/
|
||||
var/
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
@@ -21,9 +24,6 @@ hs_err_pid*
|
||||
|
||||
### Gradle ###
|
||||
.gradle
|
||||
build/
|
||||
dist/
|
||||
target/
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
+40
-20
@@ -1,7 +1,23 @@
|
||||
/**
|
||||
Local repostories:
|
||||
Unix - ~/.m2
|
||||
Windows - C:\Users\<username>\.m2
|
||||
For example - /Users/alex/.m2/repository/<library_path>/<version>/<name>.<extension>
|
||||
*/
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'application'
|
||||
mainClassName = 'com.reliancy.jabba.Router'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
group='com.reliancy'
|
||||
version = '0.1'
|
||||
mainClassName = group+'.'+name+'.Router'
|
||||
System.out.println("group:"+group);
|
||||
System.out.println("name:"+name);
|
||||
System.out.println("version:"+version);
|
||||
System.out.println("entry:"+mainClassName);
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
@@ -12,8 +28,8 @@ targetCompatibility = 1.8
|
||||
dependencies {
|
||||
implementation "org.eclipse.jetty:jetty-server:11.0.1"
|
||||
implementation "org.slf4j:slf4j-simple:2.0.0-alpha0"
|
||||
implementation 'com.hubspot.jinjava:jinjava:2.5.10'
|
||||
implementation 'com.hubspot.jinjava:jinjava:2.5.10'
|
||||
//implementation 'com.hubspot.jinjava:jinjava:2.5.10'
|
||||
implementation 'com.github.jknack:handlebars:4.2.1'
|
||||
implementation 'com.h2database:h2:1.4.200'
|
||||
// https://mvnrepository.com/artifact/org.postgresql/postgresql
|
||||
implementation 'org.postgresql:postgresql:42.3.1'
|
||||
@@ -43,20 +59,21 @@ test {
|
||||
}
|
||||
}
|
||||
jar {
|
||||
archiveBaseName = 'jabba'
|
||||
archiveVersion = '0.1'
|
||||
archiveBaseName = project.name
|
||||
archiveVersion = project.version
|
||||
manifest {
|
||||
attributes "Main-Class": mainClassName
|
||||
attributes "Class-Path": configurations.runtimeClasspath.collect { it.getName() }.join(' ')
|
||||
}
|
||||
}
|
||||
task copyToLib(type: Copy) {
|
||||
into "${buildDir}/libs" from configurations.runtimeClasspath
|
||||
//into "${buildDir}/libs" from configurations.runtimeClasspath
|
||||
into layout.buildDirectory.dir("libs") from configurations.runtimeClasspath
|
||||
}
|
||||
build.dependsOn(copyToLib)
|
||||
build.finalizedBy(copyToLib)
|
||||
task fat_jar(type: Jar) {
|
||||
archiveBaseName = 'fat-jabba'
|
||||
archiveVersion = '0.1'
|
||||
archiveBaseName = 'fat-'+project.name
|
||||
archiveVersion = project.version
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
/*
|
||||
manifest {
|
||||
@@ -86,7 +103,7 @@ class Server implements Runnable{
|
||||
}
|
||||
return singleton;
|
||||
}
|
||||
org.slf4j.Logger log=org.slf4j.LoggerFactory.getLogger("server.driver");
|
||||
//org.slf4j.Logger log=org.slf4j.LoggerFactory.getLogger("server.driver");
|
||||
Thread driver=null;
|
||||
Runnable task=null;
|
||||
protected Server(){}
|
||||
@@ -107,10 +124,8 @@ class Server implements Runnable{
|
||||
info("running task");
|
||||
try{
|
||||
task.run();
|
||||
}catch(java.lang.InterruptedException ex){
|
||||
}catch(java.lang.Exception ex){
|
||||
info("running task:interrupted");
|
||||
}catch(org.gradle.internal.UncheckedException ex2){
|
||||
info("running task:interrupted2");
|
||||
}
|
||||
}
|
||||
public Server start(Runnable c){
|
||||
@@ -165,16 +180,21 @@ task runServer{
|
||||
//args "arg1", "arg2"
|
||||
*/
|
||||
}
|
||||
// build.gradle
|
||||
eclipse.classpath {
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
eclipse{
|
||||
classpath {
|
||||
defaultOutputDir = file("build") ///default
|
||||
file.whenMerged { cp ->
|
||||
cp.entries.forEach { cpe ->
|
||||
if (cpe instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder) {
|
||||
cpe.output = cpe.output.replace "bin/", "build/classes/java/"
|
||||
}
|
||||
if (cpe instanceof org.gradle.plugins.ide.eclipse.model.Output) {
|
||||
cpe.path = cpe.path.replace "bin/", "build/"
|
||||
if (cpe.kind == 'src' && cpe.hasProperty('output')) {
|
||||
cpe.output = cpe.output.replace('bin/', "build/classes/java/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,47 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.reliancy.util.CloseableIterator;
|
||||
|
||||
/** Description of a terminal operation with a slice of dbo objects as input or output.
|
||||
* This object is not just for reading but also bulk updating.
|
||||
* It will be used to describe a multi DBO read or write and to then also track results.
|
||||
* At its core are action traits which are classes that define either loading,saving or deleting.
|
||||
* The items field is a consumable object when consumed the action is done.
|
||||
* So for loading we iterate once done it cannot be done again. Also when items are provided for saving
|
||||
* once iterated over and saved they we done.
|
||||
*/
|
||||
public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
public static enum Type{
|
||||
NONE,LOAD,SAVE,DELETE
|
||||
public class Action implements Iterable<DBO>,SiphonIterator<DBO>{
|
||||
public static class Trait{
|
||||
public String toString(){return getClass().getSimpleName();}
|
||||
}
|
||||
public static class Load extends Trait{
|
||||
int limit,offset;
|
||||
Check filter;
|
||||
}
|
||||
public static class Save extends Trait{
|
||||
|
||||
}
|
||||
public static class Delete extends Trait{
|
||||
Check filter;
|
||||
}
|
||||
|
||||
Terminal terminal;
|
||||
Type type;
|
||||
Trait trait;
|
||||
Entity entity;
|
||||
Object[] params;
|
||||
CloseableIterator<DBO> items;
|
||||
int limit,offset;
|
||||
Condition filter;
|
||||
SiphonIterator<DBO> items;
|
||||
|
||||
public Action(){
|
||||
type=Type.NONE;
|
||||
trait=null;
|
||||
}
|
||||
public Action(Type t){
|
||||
type=t;
|
||||
public Action(Trait t){
|
||||
trait=t;
|
||||
}
|
||||
public Action(Terminal t){
|
||||
terminal=t;
|
||||
type=Type.NONE;
|
||||
trait=null;
|
||||
}
|
||||
public Action execute() throws IOException{
|
||||
return terminal.execute(this);
|
||||
@@ -42,11 +54,11 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
this.terminal = terminal;
|
||||
return this;
|
||||
}
|
||||
public Type getType() {
|
||||
return type;
|
||||
public Trait getTrait() {
|
||||
return trait;
|
||||
}
|
||||
public Action setType(Type type) {
|
||||
this.type = type;
|
||||
public Action setTrait(Trait t) {
|
||||
this.trait = t;
|
||||
return this;
|
||||
}
|
||||
public Entity getEntity() {
|
||||
@@ -58,22 +70,27 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
}
|
||||
public void clear(){
|
||||
terminal=null;
|
||||
type=Type.NONE;
|
||||
trait=null;
|
||||
entity=null;
|
||||
setItems((DBO)null);
|
||||
}
|
||||
public Action load(Entity ent){
|
||||
type=Type.LOAD;
|
||||
trait=new Load();
|
||||
entity=ent;
|
||||
return this;
|
||||
}
|
||||
public Action load(Class<? extends DBO> cls){
|
||||
trait=new Load();
|
||||
entity=Entity.recall(cls);
|
||||
return this;
|
||||
}
|
||||
public Action save(Entity ent){
|
||||
type=Type.SAVE;
|
||||
trait=new Save();
|
||||
entity=ent;
|
||||
return this;
|
||||
}
|
||||
public Action delete(Entity ent){
|
||||
type=Type.DELETE;
|
||||
trait=new Delete();
|
||||
entity=ent;
|
||||
return this;
|
||||
}
|
||||
@@ -81,10 +98,10 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
params=p;
|
||||
return this;
|
||||
}
|
||||
public Action setItems(DBO ...itms){
|
||||
CloseableIterator<DBO> it=null;
|
||||
public Action setItems(final DBO ...itms){
|
||||
SiphonIterator<DBO> it=null;
|
||||
if(itms!=null){
|
||||
it=new CloseableIterator<DBO>() {
|
||||
it=new SiphonIterator<DBO>() {
|
||||
private int index = 0;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
@@ -101,7 +118,28 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
}
|
||||
return setItems(it);
|
||||
}
|
||||
public Action setItems(CloseableIterator<DBO> itms){
|
||||
public Action setItems(final Collection<DBO> itms){
|
||||
SiphonIterator<DBO> it=null;
|
||||
if(itms!=null){
|
||||
it=new SiphonIterator<DBO>() {
|
||||
private final Iterator<DBO> str = itms.iterator();
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return str.hasNext();
|
||||
}
|
||||
@Override
|
||||
public DBO next() {
|
||||
return str.next();
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
};
|
||||
}
|
||||
return setItems(it);
|
||||
}
|
||||
public Action setItems(SiphonIterator<DBO> itms){
|
||||
if(items==itms) return this;
|
||||
if(items!=null){
|
||||
try {
|
||||
items.close();
|
||||
@@ -111,7 +149,7 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
items=itms;
|
||||
return this;
|
||||
}
|
||||
protected CloseableIterator<DBO> getItems(){
|
||||
protected SiphonIterator<DBO> getItems(){
|
||||
return items;
|
||||
}
|
||||
@Override
|
||||
@@ -130,30 +168,51 @@ public class Action implements Iterable<DBO>,CloseableIterator<DBO>{
|
||||
public void close() throws IOException {
|
||||
if(items!=null){
|
||||
items.close();
|
||||
items=null;
|
||||
if(terminal!=null) terminal.end(this);
|
||||
}
|
||||
items=null;
|
||||
}
|
||||
public Action limit(int max) {
|
||||
limit=max;
|
||||
((Load)trait).limit=max;
|
||||
return this;
|
||||
}
|
||||
public Action if_filter(Condition... c){
|
||||
public Action filterBy(Check... c){
|
||||
Check filter=null;
|
||||
if(c!=null){
|
||||
if(c.length>1) filter=Condition.and(c);
|
||||
if(c.length>1) filter=Check.and(c);
|
||||
else filter=c[0];
|
||||
}
|
||||
if(trait instanceof Load){
|
||||
((Load)trait).filter=filter;
|
||||
}else
|
||||
if(trait instanceof Delete){
|
||||
((Delete)trait).filter=filter;
|
||||
}else{
|
||||
filter=null;
|
||||
throw new IllegalStateException("filtering not supported by trait:"+trait);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Action if_pk(Object[] id) {
|
||||
public Check getFilter(){
|
||||
if(trait instanceof Load){
|
||||
return ((Load)trait).filter;
|
||||
}else
|
||||
if(trait instanceof Delete){
|
||||
return ((Delete)trait).filter;
|
||||
}else{
|
||||
throw new IllegalStateException("filtering not supported by trait:"+trait);
|
||||
}
|
||||
}
|
||||
public Action if_pk(Object... id) {
|
||||
Field pk=entity.getPk();
|
||||
return if_filter(Condition.eq(pk,id));
|
||||
return filterBy(pk.eq(id));
|
||||
}
|
||||
public DBO first() {
|
||||
try{
|
||||
return items!=null?items.next():null;
|
||||
if(this.hasNext()){
|
||||
return this.next();
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}finally{
|
||||
clear();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/** constraint on a field.
|
||||
* conditions can be leafs or groups such as and,or,not
|
||||
*/
|
||||
public class Check implements Iterable<Check> {
|
||||
public static abstract class Op{
|
||||
public abstract boolean met(Check c,Object val);
|
||||
}
|
||||
public static Op AND=new Op(){
|
||||
public String toString(){return "AND";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op OR=new Op(){
|
||||
public String toString(){return "OR";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op NOT=new Op(){
|
||||
public String toString(){return "NOT";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op EQ=new Op(){
|
||||
public String toString(){return "=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op NEQ=new Op(){
|
||||
public String toString(){return "<>";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op GT=new Op(){
|
||||
public String toString(){return ">";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op GTE=new Op(){
|
||||
public String toString(){return ">=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LT=new Op(){
|
||||
public String toString(){return "<";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LTE=new Op(){
|
||||
public String toString(){return "<=";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LIKE=new Op(){
|
||||
public String toString(){return "LIKE";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op IN=new Op(){
|
||||
public String toString(){return "IN";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op NOT_IN=new Op(){
|
||||
public String toString(){return "NOT IN";}
|
||||
public boolean met(Check c,Object val){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static class CheckIterator implements Iterator<Check>{
|
||||
final Check root;
|
||||
Check cur;
|
||||
int index;
|
||||
public CheckIterator(Check ch){
|
||||
root=ch;
|
||||
cur=root;
|
||||
index=0;
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return cur.isLeaf()==false && index<cur.args.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Check next() {
|
||||
return (Check)cur.args[index++];
|
||||
}
|
||||
|
||||
}
|
||||
public static Check and(Check... c) {
|
||||
return new Check(AND,c);
|
||||
}
|
||||
public static Check all(Check... c) {
|
||||
return new Check(AND,c);
|
||||
}
|
||||
public static Check or(Check... c) {
|
||||
return new Check(OR,c);
|
||||
}
|
||||
public static Check any(Check... c) {
|
||||
return new Check(OR,c);
|
||||
}
|
||||
public static Check not(Check... c) {
|
||||
return new Check(NOT,c);
|
||||
}
|
||||
public static Check none(Check... c) {
|
||||
return new Check(NOT,c);
|
||||
}
|
||||
public static Check eq(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(EQ,pk,id);
|
||||
}
|
||||
public static Check neq(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(NEQ,pk,id);
|
||||
}
|
||||
public static Check gt(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(GT,pk,id);
|
||||
}
|
||||
public static Check gte(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(GTE,pk,id);
|
||||
}
|
||||
public static Check lt(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(LT,pk,id);
|
||||
}
|
||||
public static Check lte(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(LTE,pk,id);
|
||||
}
|
||||
public static Check like(Field pk, Object... args) {
|
||||
Object id=args;
|
||||
if(id!=null && args.length==1) id=args[0];
|
||||
return new Check(LIKE,pk,id);
|
||||
}
|
||||
public static Check in(Field pk, Object... id) {
|
||||
return new Check(IN,pk,id);
|
||||
}
|
||||
public static Check not_in(Field pk, Object... id) {
|
||||
return new Check(NOT_IN,pk,id);
|
||||
}
|
||||
Op code;
|
||||
boolean leaf;
|
||||
Object[] args;
|
||||
boolean locked;
|
||||
|
||||
public Check(Op code,Field f,Object val){
|
||||
this.code=code;
|
||||
leaf=true;
|
||||
args=new Object[]{f,val};
|
||||
}
|
||||
public Check(Op code,Check ... sub){
|
||||
this.code=code;
|
||||
leaf=false;
|
||||
args=sub;
|
||||
}
|
||||
public Check setLocked(boolean f){
|
||||
locked=f;
|
||||
return this;
|
||||
}
|
||||
public boolean isLocked(){
|
||||
return locked;
|
||||
}
|
||||
public Op getCode(){
|
||||
return code;
|
||||
}
|
||||
public boolean isLeaf(){
|
||||
return leaf;
|
||||
}
|
||||
public boolean met(Object val){
|
||||
return code.met(this,val);
|
||||
}
|
||||
@Override
|
||||
public Iterator<Check> iterator() {
|
||||
return new CheckIterator(this);
|
||||
}
|
||||
public int getChildCount(){
|
||||
return leaf?0:args.length;
|
||||
}
|
||||
public Check getChild(int index){
|
||||
return leaf?null:(Check)args[index];
|
||||
}
|
||||
public Field getField(){
|
||||
return (Field)args[0];
|
||||
}
|
||||
public Object getValue(){
|
||||
return (Object)args[1];
|
||||
}
|
||||
public Check setValue(Object val){
|
||||
if(locked) throw new IllegalStateException("check value is locked");
|
||||
if(!leaf) throw new IllegalStateException("check is not a leaf");
|
||||
args[1]=val;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
/** constraint on a field.
|
||||
* conditions can be leafs or groups such as and,or,not
|
||||
*/
|
||||
public class Condition {
|
||||
public static abstract class Op{
|
||||
public abstract boolean met(Condition c);
|
||||
}
|
||||
public static Op AND=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op OR=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op NOT=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op EQ=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op NEQ=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op GT=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op GTE=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LT=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LTE=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op LIKE=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Op IN=new Op(){
|
||||
public boolean met(Condition c){
|
||||
return true;
|
||||
}
|
||||
};
|
||||
public static Condition and(Condition... c) {
|
||||
return new Condition(AND,c);
|
||||
}
|
||||
public static Condition eq(Field pk, Object... id) {
|
||||
return new Condition(EQ,pk,id);
|
||||
}
|
||||
Op code;
|
||||
Object[] args;
|
||||
public Condition(Op code,Field f,Object val){
|
||||
this.code=code;
|
||||
args=new Object[]{f,val};
|
||||
}
|
||||
public Condition(Op code,Condition ... sub){
|
||||
this.code=code;
|
||||
args=sub;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,119 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.reliancy.rec.Hdr;
|
||||
import com.reliancy.rec.JSON;
|
||||
import com.reliancy.rec.Rec;
|
||||
import com.reliancy.rec.Slot;
|
||||
|
||||
/** Instance of an entity, usually a row in a table.
|
||||
*
|
||||
*/
|
||||
public class DBO{
|
||||
public class DBO implements Rec{
|
||||
public static enum Status{
|
||||
NEW,USED,DELETED,COMPUTED
|
||||
}
|
||||
Terminal terminal;
|
||||
Entity type;
|
||||
Status status;
|
||||
Object[] values;
|
||||
|
||||
public DBO() {
|
||||
Class<? extends DBO> cls=this.getClass();
|
||||
if(cls!=DBO.class){
|
||||
Entity ent=Entity.recall(cls);
|
||||
setType(ent);
|
||||
}
|
||||
status=Status.NEW;
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
try {
|
||||
StringBuffer ret=new StringBuffer();
|
||||
JSON.writes(this,ret);
|
||||
return ret.toString();
|
||||
} catch (IOException e) {
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
public Terminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
public void setTerminal(Terminal terminal) {
|
||||
public DBO setTerminal(Terminal terminal) {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
public Entity getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(Entity type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
public Status getStatus(){
|
||||
return status;
|
||||
}
|
||||
public void setStatus(Status s) {
|
||||
public DBO setStatus(Status s) {
|
||||
this.status = s;
|
||||
return this;
|
||||
}
|
||||
public final Entity getType() {
|
||||
return type;
|
||||
}
|
||||
public final DBO setType(Entity type) {
|
||||
this.type = type;
|
||||
if(type==null){
|
||||
values=null;
|
||||
}else{
|
||||
values=new Object[type.count()];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Hdr meta() {
|
||||
return type;
|
||||
}
|
||||
@Override
|
||||
public int count() {
|
||||
return values!=null?values.length:0;
|
||||
}
|
||||
@Override
|
||||
public Rec set(int pos, Object val) {
|
||||
if(pos<0) pos=count()+pos;
|
||||
values[pos]=val;
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Object get(int pos) {
|
||||
if(pos<0) pos=count()+pos;
|
||||
return values[pos];
|
||||
}
|
||||
@Override
|
||||
public Rec add(Object val) {
|
||||
throw new UnsupportedOperationException("dbo is not array");
|
||||
}
|
||||
@Override
|
||||
public Rec remove(int s) {
|
||||
throw new UnsupportedOperationException("dbo is not array");
|
||||
}
|
||||
@Override
|
||||
public Rec set(Slot s, Object val) {
|
||||
if(s==null) throw new IllegalArgumentException("invalid key provided");
|
||||
int index=s.getPosition(); // try slot position
|
||||
//if(index<0) index=type.findSlot(s.getName());// fall back to search if slot not set
|
||||
if(index<0){
|
||||
throw new IllegalArgumentException("invalid key provided:"+s.getName());
|
||||
}else{
|
||||
values[index]=val;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public Object get(Slot s, Object def) {
|
||||
if(s==null) throw new IllegalArgumentException("invalid key provided");
|
||||
int index=s.getPosition(); // try slot position
|
||||
//if(index<0) index=type.findSlot(s.getName());// fall back to search if slot not set
|
||||
if(index<0) throw new IllegalArgumentException("invalid key provided:"+s.getName());
|
||||
Object ret=values[index];
|
||||
return ret==null?def:ret;
|
||||
}
|
||||
@Override
|
||||
public Rec remove(Slot s) {
|
||||
throw new UnsupportedOperationException("dbo is not resizable");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,52 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.reliancy.rec.Hdr;
|
||||
import com.reliancy.rec.Slot;
|
||||
|
||||
/** Describes an object structure, usually a table.
|
||||
*
|
||||
*/
|
||||
public class Entity extends Hdr{
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public static @interface Info {
|
||||
String name();
|
||||
}
|
||||
static final HashMap<String,Entity> registry=new HashMap<>();
|
||||
public static final void publish(Entity ent){
|
||||
registry.put(ent.getName(),ent);
|
||||
registry.put(ent.getId(),ent);
|
||||
}
|
||||
public static final void retract(Entity ent){
|
||||
registry.values().remove(ent);
|
||||
if(ent==null) return;
|
||||
Collection<Entity> vals=registry.values();
|
||||
while(vals.remove(ent)){}
|
||||
}
|
||||
public static final void retract(Class<? extends DBO> cls){
|
||||
Entity ent=recall(cls.getSimpleName());
|
||||
if(ent!=null){
|
||||
retract(ent);
|
||||
}
|
||||
}
|
||||
public static final Entity recall(String name){
|
||||
return registry.get(name);
|
||||
}
|
||||
public static final Entity recall(Class<?> cls){
|
||||
return recall(cls.getSimpleName());
|
||||
|
||||
public static final Entity recall(Class<? extends DBO> cls){
|
||||
Entity ent=recall(cls.getSimpleName());
|
||||
if(ent==null){
|
||||
ent=publish(cls);
|
||||
}
|
||||
return ent;
|
||||
}
|
||||
/**
|
||||
* this method will analyze a DBO class and forumate an Entity object out of it.
|
||||
@@ -27,15 +54,124 @@ public class Entity extends Hdr{
|
||||
* @return
|
||||
*/
|
||||
public static final Entity publish(Class<? extends DBO> cls){
|
||||
return null;
|
||||
Entity ret=registry.get(cls.getSimpleName());
|
||||
if(ret!=null) return ret;
|
||||
//System.out.println("Analyzing:"+cls);
|
||||
Class base=cls.getSuperclass();
|
||||
Entity base_ent=null;
|
||||
int position0=0;
|
||||
if(base!=null && base!=DBO.class){
|
||||
base_ent=publish(base);
|
||||
position0=base_ent.count();
|
||||
}
|
||||
java.lang.reflect.Field[] declaredFields = cls.getDeclaredFields();
|
||||
ArrayList<Field> slots=new ArrayList<>();
|
||||
for (java.lang.reflect.Field field : declaredFields) {
|
||||
if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
String sf_name=field.getName();
|
||||
Field slot=(Field) field.get(cls);
|
||||
slot.setId(sf_name);
|
||||
slot.setPosition(position0+slots.size());
|
||||
slots.add(slot);
|
||||
//System.out.println(sf_name+":"+slot+" atpos:"+slot.getPosition());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
Info info=cls.getAnnotation(Info.class);
|
||||
ret=new Entity(info!=null?info.name():cls.getSimpleName()).setId(cls.getSimpleName());
|
||||
ret.setBase(base_ent);
|
||||
ret.setType(cls);
|
||||
ret.getOwnSlots().addAll(slots);
|
||||
publish(ret);
|
||||
return ret;
|
||||
}
|
||||
Entity base;
|
||||
String dbName;
|
||||
String id;
|
||||
Field pk;
|
||||
public Entity(String name) {
|
||||
super(name);
|
||||
}
|
||||
@Override
|
||||
public Slot makeSlot(String name){
|
||||
return new Field(name);
|
||||
}
|
||||
@Override
|
||||
public Iterator<Slot> iterator(int offset){
|
||||
if(offset>0) throw new IllegalArgumentException("Offset not supported");
|
||||
final Entity ent=this;
|
||||
return new Iterator<Slot>(){
|
||||
final FieldSlice slice=new FieldSlice(ent).including(Field.FLAG_STORABLE);
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return slice.hasNext();
|
||||
}
|
||||
@Override
|
||||
public Slot next() {
|
||||
return slice.next();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public int count(){
|
||||
return super.count()+(base!=null?base.count():0);
|
||||
}
|
||||
/**
|
||||
* gets a slot which could be here or in base.
|
||||
*/
|
||||
@Override
|
||||
public Slot getSlot(int pos){
|
||||
if(base!=null){ // we got base
|
||||
int ofs=base.count();
|
||||
if(pos<ofs) return base.getSlot(pos);
|
||||
else return super.getSlot(pos-ofs);
|
||||
}else{ // regular no base
|
||||
return super.getSlot(pos);
|
||||
}
|
||||
}
|
||||
public Field getField(int index){
|
||||
return (Field)getSlot(index);
|
||||
}
|
||||
public int getDepth(){
|
||||
return base!=null?1+base.getDepth():0;
|
||||
}
|
||||
public Entity getBase() {
|
||||
return base;
|
||||
}
|
||||
public Entity setBase(Entity base) {
|
||||
this.base = base;
|
||||
return this;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public Entity setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
public Entity setPk(Field pk) {
|
||||
this.pk = pk;
|
||||
return this;
|
||||
}
|
||||
public Field getPk(){
|
||||
if(pk!=null) return pk;
|
||||
// try to locate the pk - this now gos over base as well
|
||||
for(int i=0;i<count() && pk==null;i++){
|
||||
Field pp=(Field) getSlot(i);
|
||||
if(pp.isPk()) pk=pp;
|
||||
}
|
||||
return pk;
|
||||
}
|
||||
public DBO newInstance() throws InstantiationException, IllegalAccessException{
|
||||
return newInstance(null).setStatus(DBO.Status.NEW);
|
||||
}
|
||||
public DBO newInstance(Terminal t) throws InstantiationException, IllegalAccessException{
|
||||
Class<?> cls=getType();
|
||||
DBO ret=(DBO) cls.newInstance();
|
||||
ret.setType(this).setTerminal(t).setStatus(DBO.Status.NEW);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,101 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Date;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import com.reliancy.rec.Slot;
|
||||
/**
|
||||
* Description of a column or property.
|
||||
*/
|
||||
public class Field extends Slot {
|
||||
|
||||
public static Field Int(String name){
|
||||
return new Field(name,Integer.class);
|
||||
}
|
||||
public static Field Str(String name){
|
||||
return new Field(name,String.class);
|
||||
}
|
||||
public static Field Bool(String name){
|
||||
return new Field(name,Boolean.class);
|
||||
}
|
||||
public static Field Float(String name){
|
||||
return new Field(name,Float.class);
|
||||
}
|
||||
public static Field Num(String name){
|
||||
return new Field(name,BigDecimal.class);
|
||||
}
|
||||
public static Field Date(String name){
|
||||
return new Field(name,Date.class);
|
||||
}
|
||||
public static Field DateTime(String name){
|
||||
return new Field(name,Timestamp.class);
|
||||
}
|
||||
public static final int FLAG_PK =0x0100;
|
||||
public static final int FLAG_AUTOINC =0x0200;
|
||||
String id;
|
||||
String typeParams;
|
||||
public Field(String name) {
|
||||
super(name);
|
||||
this.raiseFlags(Field.FLAG_STORABLE);
|
||||
}
|
||||
public Field(String name,Class<?> typ) {
|
||||
super(name,typ);
|
||||
this.raiseFlags(Field.FLAG_STORABLE);
|
||||
}
|
||||
@Override
|
||||
public boolean equals(String str){
|
||||
return super.equals(str) || (id!=null && id.equalsIgnoreCase(str));
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public boolean isPk() {
|
||||
return checkFlags(FLAG_PK);
|
||||
}
|
||||
public Field setPk(boolean pk) {
|
||||
if(pk) raiseFlags(FLAG_PK); else clearFlags(FLAG_PK);
|
||||
return this;
|
||||
}
|
||||
public boolean isAutoIncrement() {
|
||||
return checkFlags(FLAG_AUTOINC);
|
||||
}
|
||||
public Field setAutoIncrement(boolean pk) {
|
||||
if(pk) raiseFlags(FLAG_AUTOINC); else clearFlags(FLAG_AUTOINC);
|
||||
return this;
|
||||
}
|
||||
public String getTypeParams() {
|
||||
return typeParams;
|
||||
}
|
||||
public Field setTypeParams(String p) {
|
||||
typeParams=p;
|
||||
return this;
|
||||
}
|
||||
public Check eq(Object... val) {
|
||||
return Check.eq(this,val);
|
||||
}
|
||||
public Check neq(Object... val) {
|
||||
return Check.neq(this,val);
|
||||
}
|
||||
public Check gt(Object... val) {
|
||||
return Check.gt(this,val);
|
||||
}
|
||||
public Check gte(Object... val) {
|
||||
return Check.gte(this,val);
|
||||
}
|
||||
public Check lt(Object... val) {
|
||||
return Check.lt(this,val);
|
||||
}
|
||||
public Check lte(Object... val) {
|
||||
return Check.lte(this,val);
|
||||
}
|
||||
public Check like(Object... val) {
|
||||
return Check.like(this,val);
|
||||
}
|
||||
public Check in(Object... val) {
|
||||
return Check.in(this,val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.reliancy.dbo;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/** Field iterator matching flags over entity hierarchy.
|
||||
*
|
||||
*/public class FieldSlice implements Iterator<Field>,Iterable<Field>{
|
||||
protected final Entity entity;
|
||||
protected FieldSlice sup;
|
||||
protected int includeMask;
|
||||
protected int excludeMask;
|
||||
protected int raw_index;
|
||||
protected Field next_field;
|
||||
protected int next_index;
|
||||
protected Entity next_entity;
|
||||
public FieldSlice(Entity ent){
|
||||
entity=ent;
|
||||
if(entity.getBase()!=null){
|
||||
sup=new FieldSlice(ent.getBase());
|
||||
}else{
|
||||
sup=null;
|
||||
}
|
||||
raw_index=-1;
|
||||
next_index=-1;
|
||||
}
|
||||
public FieldSlice including(int mask){
|
||||
includeMask=mask;
|
||||
if(sup!=null) sup.including(mask);
|
||||
return this;
|
||||
}
|
||||
public FieldSlice excluding(int mask){
|
||||
excludeMask=mask;
|
||||
if(sup!=null) sup.excluding(mask);
|
||||
return this;
|
||||
}
|
||||
/** we add rewind capability to allow reuse of same fieldslice.
|
||||
* i.e we use it to generate recipe, then later to properly enumerate values.
|
||||
*/
|
||||
public FieldSlice rewind(){
|
||||
raw_index=-1;
|
||||
next_index=-1;
|
||||
next_field=null;
|
||||
next_entity=null;
|
||||
if(sup!=null) sup.rewind();
|
||||
return this;
|
||||
}
|
||||
// search for next valid field
|
||||
public final Field findNext(){
|
||||
List<?> local=entity.getOwnSlots();
|
||||
if(raw_index>=local.size()) return null; // we have exhausted local supply
|
||||
next_field=null; // clear prev result
|
||||
// search at base
|
||||
if(sup!=null && sup.hasNext()){
|
||||
next_field=sup.next();
|
||||
next_index++;
|
||||
next_entity=sup.nextEntity();
|
||||
return next_field;
|
||||
}
|
||||
next_entity=entity;
|
||||
// now search locally
|
||||
for(raw_index=raw_index+1;raw_index<local.size();raw_index++){
|
||||
Field f=(Field) local.get(raw_index);
|
||||
int attr=f.getFlags();
|
||||
if((attr & excludeMask)!=0) continue; // skip if in exluding set
|
||||
if((attr & includeMask)==0) continue; // skip if not in including set
|
||||
next_field=f;
|
||||
next_index+=1;
|
||||
break;
|
||||
}
|
||||
return next_field;
|
||||
}
|
||||
@Override
|
||||
public Iterator<Field> iterator() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
Field next=findNext();
|
||||
return next!=null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field next() {
|
||||
return next_field;
|
||||
}
|
||||
|
||||
public int nextIndex(){
|
||||
return next_field!=null?next_index:-1;
|
||||
}
|
||||
public Entity nextEntity(){
|
||||
return next_entity;
|
||||
}
|
||||
public DBO makeRecord() throws InstantiationException, IllegalAccessException{
|
||||
return entity.newInstance();
|
||||
}
|
||||
public void writeRecord(DBO rec,Object val){
|
||||
rec.set(next_field, val);
|
||||
}
|
||||
public Object readRecord(DBO rec,Object def){
|
||||
return rec.get(next_field, def);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
public final class SQL implements Appendable{
|
||||
public final static Object NULL=new Object();
|
||||
public final static String WS=" ";
|
||||
public final static String SELECT="SELECT";
|
||||
public final static String INSERT="INSERT INTO";
|
||||
public final static String UPDATE="UPDATE";
|
||||
public final static String DELETE="DELETE";
|
||||
public final static String FROM="FROM";
|
||||
public final static String INNER_JOIN="INNER JOIN";
|
||||
public final static String ON="ON";
|
||||
public final static String SET="SET";
|
||||
public final static String WHERE="WHERE";
|
||||
|
||||
final StringBuffer buffer=new StringBuffer();
|
||||
final SQLTerminal terminal;
|
||||
final String ql,qr;
|
||||
final HashMap<Entity,String> entAlias=new HashMap<>();
|
||||
|
||||
public SQL(SQLTerminal terminal){
|
||||
this.terminal=terminal;
|
||||
ql=terminal!=null?terminal.getQuoteLeft():"\"";
|
||||
qr=terminal!=null?terminal.getQuoteRight():"\"";
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return buffer.toString();
|
||||
}
|
||||
@Override
|
||||
public final SQL append(CharSequence csq){
|
||||
buffer.append(csq);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public final SQL append(CharSequence csq, int start, int end){
|
||||
buffer.append(csq,start,end);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public final SQL append(char c){
|
||||
buffer.append(c);
|
||||
return this;
|
||||
}
|
||||
public final SQL select(){
|
||||
append(SELECT);
|
||||
return this;
|
||||
}
|
||||
public final SQL insert(){
|
||||
append(INSERT);
|
||||
return this;
|
||||
}
|
||||
public final SQL update(){
|
||||
append(UPDATE);
|
||||
return this;
|
||||
}
|
||||
public final SQL delete(){
|
||||
append(DELETE);
|
||||
return this;
|
||||
}
|
||||
public final SQL from(){
|
||||
append(WS).append(FROM).append(WS);
|
||||
return this;
|
||||
}
|
||||
public final SQL inner_join(){
|
||||
append(WS).append(INNER_JOIN).append(WS);
|
||||
return this;
|
||||
}
|
||||
public final SQL on(){
|
||||
append(WS).append(ON).append(WS);
|
||||
return this;
|
||||
}
|
||||
public final String wrap(String id){
|
||||
if(id.startsWith(ql) && id.endsWith(qr)){
|
||||
return id;
|
||||
}else{
|
||||
return ql+id.replace(".",qr+"."+ql)+qr;
|
||||
}
|
||||
}
|
||||
public final SQL id(String id){
|
||||
if(id.startsWith(ql) && id.endsWith(qr)){
|
||||
append(id);
|
||||
}else{
|
||||
append(ql).append(id.replace(".",qr+"."+ql)).append(qr);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public final String getAlias(Entity e){
|
||||
String eAlias=entAlias.get(e);
|
||||
if(eAlias!=null) return eAlias;
|
||||
eAlias="e"+entAlias.size();
|
||||
entAlias.put(e,eAlias);
|
||||
return eAlias;
|
||||
}
|
||||
public final SQL select(Entity ent,FieldSlice fit){
|
||||
entAlias.clear();;
|
||||
select();
|
||||
while(fit.hasNext()){
|
||||
int index=fit.nextIndex();
|
||||
Field f=fit.next();
|
||||
Entity e=fit.nextEntity();
|
||||
String alias=getAlias(e);
|
||||
//System.out.println("It:"+index+":/"+f+"/"+e+"/"+alias);
|
||||
append(index==0?" ":",");
|
||||
append(alias).append(".").id(f.getName());
|
||||
}
|
||||
from();
|
||||
String eAlias=getAlias(ent);
|
||||
id(ent.getName()).append(" ").append(eAlias);
|
||||
for(Entity b=ent.getBase();b!=null;b=b.getBase()){
|
||||
String bAlias=getAlias(b);
|
||||
inner_join();
|
||||
id(b.getName()).append(" ").append(bAlias);
|
||||
on();
|
||||
Field bPk=b.getPk();
|
||||
Field ePk=ent.getPk();
|
||||
append(eAlias).append(".").id(ePk.getName());
|
||||
append("=");
|
||||
append(bAlias).append(".").id(bPk.getName());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public final SQL where(){
|
||||
append(WS).append(WHERE).append(WS);
|
||||
return this;
|
||||
}
|
||||
public final SQL where(Check filter) {
|
||||
append(WS).append(WHERE).append(WS).check(filter);
|
||||
return this;
|
||||
}
|
||||
/// using entalias locate field entity and its prefix
|
||||
public final String getFieldPrefix(Field f){
|
||||
for(Map.Entry<Entity,String> e:entAlias.entrySet()){
|
||||
Entity ent=e.getKey();
|
||||
String prefix=e.getValue();
|
||||
if(ent.isOwned(f)) return prefix+".";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public final SQL check(Check filter) {
|
||||
if(filter.isLeaf()){
|
||||
Check.Op op=filter.getCode();
|
||||
Field field=filter.getField();
|
||||
String fname=wrap(filter.getField().getName());
|
||||
String opname=op.toString();
|
||||
String arg="?";
|
||||
Object val=filter.getValue();
|
||||
if(op==Check.LIKE && terminal!=null && terminal.getProtocol().contains(":postgre")){
|
||||
opname="ILIKE";
|
||||
}
|
||||
if(op==Check.IN){
|
||||
arg="("+Handy.toString(val)+")";
|
||||
}
|
||||
if(Handy.isEmpty(val)){
|
||||
// if not value then shortcuircuid condition
|
||||
fname="1";
|
||||
opname="=";
|
||||
arg="1";
|
||||
}
|
||||
if(val==NULL){
|
||||
arg="NULL";
|
||||
if(op==Check.EQ) opname="IS";
|
||||
if(op==Check.NEQ) opname="IS NOT";
|
||||
}
|
||||
append("(");
|
||||
String fprefix=getFieldPrefix(field);
|
||||
append(fprefix).append(fname).append(WS).append(opname).append(WS).append(arg);
|
||||
append(")");
|
||||
}else{
|
||||
Check.Op op=filter.getCode();
|
||||
String delim=op.toString();
|
||||
if(op==Check.NOT){
|
||||
append(delim).append("(").check(filter.getChild(0)).append(")");
|
||||
}else{
|
||||
append("(");
|
||||
for(int i=0;i<filter.getChildCount();i++){
|
||||
if(i>0) append(WS).append(delim).append(WS);
|
||||
check(filter.getChild(i));
|
||||
}
|
||||
append(")");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/** fills params list with non-trivial parameters.
|
||||
* we place this method here to be as close as possible to the one which generates the sql code.
|
||||
* check and check_export must be in synch.
|
||||
* @param filter
|
||||
* @param params
|
||||
*/
|
||||
public final void check_export(Check filter,List<Object> params) {
|
||||
if(filter.isLeaf()){
|
||||
Check.Op op=filter.getCode();
|
||||
Object val=filter.getValue();
|
||||
if(Handy.isEmpty(val) || val==NULL || op==Check.IN) return; // skip over empty or NULL values
|
||||
params.add(val);
|
||||
}else{
|
||||
for(Check ch:filter) check_export(ch,params);
|
||||
}
|
||||
}
|
||||
/** fills check values from dbo record where equal and not-equal are used.
|
||||
* we place this method here to be as close as possible to the one which generates the sql code.
|
||||
* check and check_import must be in synch.
|
||||
* @param filter
|
||||
* @param params
|
||||
*/
|
||||
public final void check_import(Check filter,DBO rec) {
|
||||
if(filter.isLeaf()){
|
||||
if(filter.isLocked()) return; // no import on locked condition
|
||||
Check.Op op=filter.getCode();
|
||||
if(op!=Check.EQ && op!=Check.NEQ) return; // skip over all conditions except = and <>
|
||||
Field f=filter.getField();
|
||||
Object val=f.get(rec,null);
|
||||
filter.setValue(val);
|
||||
}else{
|
||||
for(Check ch:filter) check_import(ch,rec);
|
||||
}
|
||||
}
|
||||
public final SQL insert(Entity entity,List<Field> supplied){
|
||||
insert();
|
||||
append(SQL.WS).id(entity.getName()).append(" (");
|
||||
StringBuffer ext=new StringBuffer();
|
||||
String delim="";
|
||||
Field pk=entity.getPk();
|
||||
if(!entity.isOwned(pk)){
|
||||
append(delim).id(pk.getName());
|
||||
ext.append(delim).append("?");
|
||||
delim=",";
|
||||
}
|
||||
for(int index=0;index<supplied.size();index++){
|
||||
Field f=supplied.get(index);
|
||||
if(index>0) delim=",";
|
||||
append(delim).id(f.getName());
|
||||
ext.append(delim).append("?");
|
||||
}
|
||||
append(") VALUES (").append(ext).append(")");
|
||||
return this;
|
||||
}
|
||||
public final SQL update(Entity entity,List<Field> supplied){
|
||||
update();
|
||||
append(SQL.WS).id(entity.getName()).append(" SET ");
|
||||
for(int index=0;index<supplied.size();index++){
|
||||
Field f=supplied.get(index);
|
||||
String delim=index==0?"":",";
|
||||
append(delim);
|
||||
id(f.getName()).append("=?");
|
||||
}
|
||||
where();
|
||||
Field pk=entity.getPk();
|
||||
id(pk.getName()).append("=?");
|
||||
return this;
|
||||
}
|
||||
public final SQL delete(Entity entity){
|
||||
delete().from().id(entity.getName());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/** Helper object which impleents DBO deleting.
|
||||
* It manages the recipe and the prepared statmenet.
|
||||
* The cleaner works in two ways.
|
||||
* If you supply items iterator it will delete by pk id those items.
|
||||
* If you supply a Check filter and no items then it will delete based on a where statement.
|
||||
*/
|
||||
public class SQLCleaner implements Closeable{
|
||||
protected final Entity entity;
|
||||
protected final SQLTerminal terminal;
|
||||
protected final SQLCleaner base; /// used for nesting
|
||||
protected final SQL sql;
|
||||
protected final ArrayList<Object> params;
|
||||
protected Check filter;
|
||||
protected Connection external;
|
||||
protected PreparedStatement deleteStmt;
|
||||
protected int itemsDeleted;
|
||||
protected Exception error;
|
||||
|
||||
public SQLCleaner(Entity ent,SQLTerminal t) {
|
||||
entity=ent;
|
||||
terminal=t;
|
||||
base=(entity.getBase()!=null)?new SQLCleaner(entity.getBase(),t):null;
|
||||
sql=new SQL(terminal);
|
||||
params=new ArrayList<>();
|
||||
}
|
||||
public SQL compileRecipe(){
|
||||
if(filter==null){
|
||||
// no filter we go with PK
|
||||
Field pk=entity.getPk();
|
||||
filter=pk.eq("?");
|
||||
}
|
||||
sql.delete(entity).where(filter);
|
||||
return sql;
|
||||
}
|
||||
public boolean isLinkExternal(){
|
||||
return external!=null;
|
||||
}
|
||||
public SQLCleaner setExternalLink(Connection link){
|
||||
external=link;
|
||||
return this;
|
||||
}
|
||||
protected Connection getExternalLink(){
|
||||
return external;
|
||||
}
|
||||
protected Connection getInternalLink(){
|
||||
try{
|
||||
if(deleteStmt!=null) return deleteStmt.getConnection();
|
||||
}catch(SQLException ex){
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public SQLCleaner open() throws SQLException{
|
||||
return open(null);
|
||||
}
|
||||
public SQLCleaner open(Check where) throws SQLException{
|
||||
this.filter=where;
|
||||
Connection link=isLinkExternal()?getExternalLink():terminal.getConnection();
|
||||
if(base!=null) base.setExternalLink(link).open(filter); // definitely external link for base
|
||||
SQL delSQL=compileRecipe();
|
||||
//System.out.println("DEL:"+delSQL+"/"+filter);
|
||||
deleteStmt=link.prepareStatement(delSQL.toString());
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException{
|
||||
if(base!=null) base.close(); // since link is external it will not close link just the rest
|
||||
Connection link=getInternalLink();
|
||||
if(deleteStmt!=null){
|
||||
try{
|
||||
deleteStmt.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
}
|
||||
try{
|
||||
if(link!=null && !isLinkExternal()) link.close();
|
||||
external=null;
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
if(error!=null){
|
||||
if(error instanceof IOException) throw (IOException)error;
|
||||
else throw new IOException(error);
|
||||
}
|
||||
|
||||
}
|
||||
public void flush(Iterator<DBO> items) throws SQLException {
|
||||
Connection link=getInternalLink();
|
||||
boolean autocommited=link.getAutoCommit();
|
||||
try{
|
||||
link.setAutoCommit(false);
|
||||
if(items==null){ // deleting by filter
|
||||
throw new SQLException("delete by filter not implemented yet");
|
||||
// we would need to leave the primary filter
|
||||
// we would use filter in an ID in (SUBQUERY)
|
||||
// this is because filter could reference all entities and we have inheritance so multiple
|
||||
// we would generate a select statement with filter and selecting only ID
|
||||
}else{ // deleting by incoming records
|
||||
while(items.hasNext()){
|
||||
DBO rec=items.next();
|
||||
deleteRecord(rec);
|
||||
}
|
||||
}
|
||||
if(!link.getAutoCommit()){
|
||||
link.commit();
|
||||
}
|
||||
}catch(SQLException ex){
|
||||
if(!link.getAutoCommit()){
|
||||
link.rollback();
|
||||
}
|
||||
throw ex;
|
||||
}finally{
|
||||
link.setAutoCommit(autocommited);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This calls one delete. It can and is called from outside in case of nesting when link is external.
|
||||
* @param rec
|
||||
* @throws SQLException
|
||||
*/
|
||||
public boolean deleteRecord(DBO rec) throws SQLException{
|
||||
if(rec==null) return false;
|
||||
if(base!=null) base.deleteRecord(rec); // save the superclass first
|
||||
sql.check_import(filter,rec); // get values from dbo
|
||||
params.clear();
|
||||
sql.check_export(filter,params); // move them into params
|
||||
for(int pindex=0;pindex<params.size();pindex++){
|
||||
Object val=params.get(pindex);
|
||||
deleteStmt.setObject(pindex+1,val);
|
||||
}
|
||||
int dcode=deleteStmt.executeUpdate();
|
||||
itemsDeleted+=dcode;
|
||||
return dcode>0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import com.reliancy.dbo.Action.Load;
|
||||
|
||||
|
||||
/** SQLIterator will delay closing a connection and will iterate over result set.
|
||||
*
|
||||
*/
|
||||
public class SQLReader implements SiphonIterator<DBO>{
|
||||
protected final Entity entity;
|
||||
protected final SQLTerminal terminal;
|
||||
protected final SQL sql;
|
||||
protected FieldSlice slice;
|
||||
protected ResultSet result;
|
||||
protected Exception error;
|
||||
|
||||
public SQLReader(Entity ent,SQLTerminal t) {
|
||||
this.entity=ent;
|
||||
terminal=t;
|
||||
// slice controls sql fields but also lets us correctly import values later
|
||||
slice=new FieldSlice(entity).including(Field.FLAG_STORABLE);
|
||||
sql=new SQL(terminal);
|
||||
}
|
||||
public SQLReader open() throws SQLException{
|
||||
return open(null);
|
||||
}
|
||||
public SQLReader open(Action action) throws SQLException{
|
||||
error=null;
|
||||
if(action==null){
|
||||
sql.select(entity,slice); // simple case
|
||||
}else{
|
||||
compileRecipe(action); // complete case
|
||||
}
|
||||
//System.out.println("SQL:"+sql);
|
||||
Connection link=terminal.getConnection();
|
||||
PreparedStatement prep=link.prepareStatement(sql.toString());
|
||||
if(action!=null){
|
||||
Load tr=(Load) action.getTrait();
|
||||
if(tr.filter!=null){
|
||||
ArrayList<Object> params=new ArrayList<>();
|
||||
sql.check_export(tr.filter, params);
|
||||
for(int pindex=0;pindex<params.size();pindex++){
|
||||
Object val=params.get(pindex);
|
||||
prep.setObject(pindex+1,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result=prep.executeQuery();
|
||||
if(link.getAutoCommit()==false) link.commit();
|
||||
//action.setItems(this); -- maybe we want multiple readers on same actions - leave this to terminal
|
||||
return this;
|
||||
}
|
||||
public SQL compileRecipe(Action action){
|
||||
sql.select(action.getEntity(),slice);
|
||||
Load tr=(Load) action.getTrait();
|
||||
if(tr.filter!=null){
|
||||
sql.where(tr.filter);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
try {
|
||||
return error==null?result.next():false;
|
||||
} catch (SQLException e) {
|
||||
error=e;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DBO next() {
|
||||
try {
|
||||
DBO ret=(DBO) slice.makeRecord();
|
||||
FieldSlice fit=slice.rewind();
|
||||
while(fit.hasNext()){
|
||||
int findex=fit.nextIndex();
|
||||
//Field field=fit.next();
|
||||
Object val=result.getObject(findex+1);
|
||||
fit.writeRecord(ret, val);
|
||||
}
|
||||
ret.setStatus(DBO.Status.USED);
|
||||
return ret;
|
||||
} catch (Exception e) {
|
||||
error=e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if(result!=null){
|
||||
Statement stmt=null;
|
||||
Connection link=null;
|
||||
try{
|
||||
stmt=result.getStatement();
|
||||
link=stmt!=null?stmt.getConnection():null;
|
||||
if(!result.isClosed()) result.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
try{
|
||||
if(stmt!=null) stmt.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
try{
|
||||
if(link!=null) link.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
}
|
||||
if(error!=null){
|
||||
if(error instanceof IOException) throw (IOException)error;
|
||||
else throw new IOException(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.JDBCType;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.reliancy.util.Path;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
@@ -12,6 +19,9 @@ public class SQLTerminal implements Terminal{
|
||||
HikariConfig config = new HikariConfig();
|
||||
HikariDataSource ds;
|
||||
Path url;
|
||||
String quoteLeft="\""; // quotes could be subject to sql flavour
|
||||
String quoteRight="\"";
|
||||
|
||||
public SQLTerminal(String url){
|
||||
this.url=new Path(url);
|
||||
String proto=this.url.getProtocol();
|
||||
@@ -20,9 +30,10 @@ public class SQLTerminal implements Terminal{
|
||||
config.setJdbcUrl(u);
|
||||
config.setUsername(this.url.getUserid());
|
||||
config.setPassword(this.url.getPassword());
|
||||
//config.setAutoCommit(false); -- do this in batch cases only
|
||||
config.addDataSourceProperty( "cachePrepStmts" , "true" );
|
||||
config.addDataSourceProperty( "prepStmtCacheSize" , "250" );
|
||||
//config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );
|
||||
config.addDataSourceProperty( "prepStmtCacheSqlLimit" , "2048" );
|
||||
ds = new HikariDataSource( config );
|
||||
}
|
||||
public Connection getConnection() throws SQLException{
|
||||
@@ -30,7 +41,179 @@ public class SQLTerminal implements Terminal{
|
||||
}
|
||||
@Override
|
||||
public Action execute(Action q) throws IOException{
|
||||
System.out.println("Executing..."+q.getTrait());
|
||||
Action.Trait tr=q.getTrait();
|
||||
if(tr instanceof Action.Load){
|
||||
Entity ent=q.getEntity();
|
||||
SQLReader reader=new SQLReader(ent,this);
|
||||
try {
|
||||
reader.open(q);
|
||||
q.setItems(reader);
|
||||
} catch (SQLException e) {
|
||||
reader.close();
|
||||
throw new IOException(e);
|
||||
}
|
||||
System.out.println("Executing...Done");
|
||||
return q;
|
||||
}else if(tr instanceof Action.Save){
|
||||
Entity ent=q.getEntity();
|
||||
try(SQLWriter writer=new SQLWriter(ent,this)) {
|
||||
writer.open();
|
||||
writer.flush(q.getItems());
|
||||
System.out.println("Executing...Done");
|
||||
return q;
|
||||
}catch(SQLException e){
|
||||
throw new IOException(e);
|
||||
}
|
||||
}else if(tr instanceof Action.Delete){
|
||||
Entity ent=q.getEntity();
|
||||
try(SQLCleaner cleaner=new SQLCleaner(ent,this)) {
|
||||
cleaner.open();
|
||||
cleaner.flush(q.getItems());
|
||||
System.out.println("Executing...Done");
|
||||
return q;
|
||||
}catch(SQLException e){
|
||||
throw new IOException(e);
|
||||
}
|
||||
}else{
|
||||
throw new UnsupportedOperationException("Trait not supported:"+tr);
|
||||
}
|
||||
}
|
||||
public String getProtocol() {
|
||||
return url.getProtocol();
|
||||
}
|
||||
public String getQuoteLeft(){
|
||||
return this.quoteLeft;
|
||||
}
|
||||
public String getQuoteRight(){
|
||||
return this.quoteRight;
|
||||
}
|
||||
final HashMap<Integer,Class<?>> sql2java=new HashMap<>();
|
||||
final HashMap<Class<?>,Integer> java2sql=new HashMap<>();
|
||||
public Map<Class<?>,Integer> getJava2SQL(){
|
||||
if(!java2sql.isEmpty()) return java2sql;
|
||||
String protocol=url.getProtocol();
|
||||
java2sql.put(java.math.BigDecimal.class,Types.DECIMAL);
|
||||
java2sql.put(java.math.BigInteger.class,Types.DECIMAL);
|
||||
java2sql.put(Boolean.class,protocol.contains(":oracle")?Types.INTEGER:Types.BOOLEAN);
|
||||
java2sql.put(Byte.class,Types.TINYINT);
|
||||
java2sql.put(Short.class,Types.SMALLINT);
|
||||
java2sql.put(Integer.class,Types.INTEGER);
|
||||
java2sql.put(Long.class,Types.BIGINT);
|
||||
java2sql.put(Float.class,Types.FLOAT);
|
||||
java2sql.put(Double.class,Types.DOUBLE);
|
||||
java2sql.put(byte[].class,Types.VARBINARY);
|
||||
java2sql.put(Blob.class,Types.BLOB);
|
||||
java2sql.put(char[].class,Types.VARCHAR);
|
||||
java2sql.put(String.class,Types.VARCHAR);
|
||||
java2sql.put(StringBuffer.class,Types.VARCHAR);
|
||||
java2sql.put(Clob.class,Types.CLOB);
|
||||
java2sql.put(java.sql.Date.class,Types.DATE);
|
||||
java2sql.put(java.sql.Time.class,Types.TIME);
|
||||
java2sql.put(java.sql.Timestamp.class,Types.TIMESTAMP);
|
||||
java2sql.put(Array.class,Types.ARRAY);
|
||||
return java2sql;
|
||||
}
|
||||
public Map<Integer,Class<?>> getSQL2Java(){
|
||||
if(!sql2java.isEmpty()) return sql2java;
|
||||
//String protocol=url.getProtocol();
|
||||
sql2java.put(Types.NUMERIC,java.math.BigDecimal.class);
|
||||
sql2java.put(Types.DECIMAL,java.math.BigDecimal.class);
|
||||
sql2java.put(Types.BIT,Boolean.class);
|
||||
sql2java.put(Types.BOOLEAN,Boolean.class);
|
||||
sql2java.put(Types.TINYINT,Byte.class);
|
||||
sql2java.put(Types.SMALLINT,Short.class);
|
||||
sql2java.put(Types.INTEGER,Integer.class);
|
||||
sql2java.put(Types.BIGINT,Long.class);
|
||||
sql2java.put(Types.REAL,Float.class);
|
||||
sql2java.put(Types.FLOAT,Float.class);
|
||||
sql2java.put(Types.DOUBLE,Double.class);
|
||||
sql2java.put(Types.BINARY,byte[].class);
|
||||
sql2java.put(Types.VARBINARY,byte[].class);
|
||||
sql2java.put(Types.LONGVARBINARY,byte[].class);
|
||||
sql2java.put(Types.CHAR,String.class);
|
||||
sql2java.put(Types.NCHAR,String.class);
|
||||
sql2java.put(Types.VARCHAR,String.class);
|
||||
sql2java.put(Types.NVARCHAR,String.class);
|
||||
sql2java.put(Types.LONGVARCHAR,String.class);
|
||||
sql2java.put(Types.LONGNVARCHAR,String.class);
|
||||
sql2java.put(Types.DATE,java.sql.Date.class);
|
||||
sql2java.put(Types.TIME,java.sql.Time.class);
|
||||
sql2java.put(Types.TIMESTAMP,java.sql.Timestamp.class);
|
||||
sql2java.put(Types.BLOB,byte[].class);
|
||||
sql2java.put(Types.CLOB,char[].class);
|
||||
sql2java.put(Types.ARRAY,java.sql.Array.class);
|
||||
sql2java.put(Types.JAVA_OBJECT,Object.class);
|
||||
return sql2java;
|
||||
}
|
||||
/**
|
||||
* Returns back java class for given id and or name.
|
||||
* The name is not used in default implementation.
|
||||
* @param typeid
|
||||
*/
|
||||
public Class<?> getJavaType(int typeid) {
|
||||
Class<?> ret=getSQL2Java().get(typeid);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* This method will correct cases when sqltype is varchar (12) but type name is date or similar.
|
||||
* @param sqltype
|
||||
* @param type_name
|
||||
* @return tries to promote sqltype given type name to something more specific.
|
||||
*/
|
||||
public int getTypeId(int sqltype,String type_name){
|
||||
if(type_name==null) return sqltype;
|
||||
type_name=type_name.toLowerCase();
|
||||
if(sqltype==Types.VARCHAR || sqltype==Types.CHAR){
|
||||
if(type_name.equals("date")) sqltype=Types.DATE;
|
||||
if(type_name.equals("time")) sqltype=Types.TIME;
|
||||
if(type_name.equals("datetime")) sqltype=Types.TIMESTAMP;
|
||||
}
|
||||
return sqltype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cls
|
||||
* @param createParams
|
||||
* @return SQL type given java class and create params
|
||||
*/
|
||||
public int getTypeId(Class<?> cls,String createParams){
|
||||
int ret=getJava2SQL().get(cls);
|
||||
return ret;
|
||||
}
|
||||
public String getTypeName(Class<?> cls,String createParams){
|
||||
int id=this.getTypeId(cls, createParams);
|
||||
String ret = JDBCType.valueOf(id).getName();
|
||||
if(ret==null) return null;
|
||||
String protocol=url.getProtocol();
|
||||
if(protocol.contains(":sqlserver")){
|
||||
if("boolean".equalsIgnoreCase(ret)) ret="BIT";
|
||||
if("timestamp".equalsIgnoreCase(ret)) ret="DATETIME";
|
||||
if("double".equalsIgnoreCase(ret)) ret="float";
|
||||
if("float".equalsIgnoreCase(ret)) ret="real";
|
||||
}
|
||||
if(protocol.contains(":postgre")){
|
||||
if("varbinary".equalsIgnoreCase(ret)) ret="bytea";
|
||||
if("double".equalsIgnoreCase(ret)) ret="double precision";
|
||||
}
|
||||
if("varchar".equalsIgnoreCase(ret) && (createParams!=null && !createParams.isEmpty())){
|
||||
long size=Long.parseLong(createParams);
|
||||
if(protocol.contains(":sqlserver")) ret=size>8000?ret.concat("(").concat("MAX").concat(")"):ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
else if(protocol.contains(":oracle")) ret=size>2000?"CLOB":ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
else if(protocol.contains(":mysql")) ret=size>Character.MAX_VALUE?"TEXT":ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
else if(protocol.contains(":h2")) ret=size>Integer.MAX_VALUE?"CLOB":ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
else if(protocol.contains(":postgre")) ret=size>Character.MAX_VALUE?"TEXT":ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
else ret=(size>Character.MAX_VALUE)?"CLOB":ret.concat("(").concat(String.valueOf(size)).concat(")");
|
||||
}
|
||||
String args=null;
|
||||
if(ret.indexOf('(')==-1 && createParams!=null && !createParams.isEmpty()){
|
||||
if("decimal".equalsIgnoreCase(ret)) args=createParams;
|
||||
if("numeric".equalsIgnoreCase(ret)) args=createParams;
|
||||
}
|
||||
if(args!=null){
|
||||
ret=ret.concat("(").concat(args).concat(")");
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.reliancy.util.Handy;
|
||||
|
||||
/** Helper object which impleents DBO saving. It manages the recipe and the prepared statmenet.
|
||||
* Also keeps track of which fields are sent down to DB.
|
||||
* The writer does the work in flush method during which it exhausts the items. While in flush it
|
||||
* will disable autocommit and enable it at the end. We could call flush multiple times to send multiple
|
||||
* batches down to DB.
|
||||
* Initially we would ctor with Action object but actually we need to generate writes for different entities
|
||||
* especially in inheritance cases.
|
||||
*/
|
||||
public class SQLWriter implements Closeable{
|
||||
protected final Entity entity;
|
||||
protected final SQLTerminal terminal;
|
||||
protected final SQLWriter base; /// used for nesting
|
||||
protected final ArrayList<Field> supplied=new ArrayList<Field>();
|
||||
protected final ArrayList<Field> generated=new ArrayList<Field>();
|
||||
protected String insertSQL;
|
||||
protected String updateSQL;
|
||||
protected Connection external;
|
||||
protected PreparedStatement insertStmt;
|
||||
protected PreparedStatement updateStmt;
|
||||
protected int itemsInserted;
|
||||
protected int itemsUpdated;
|
||||
protected Exception error;
|
||||
|
||||
public SQLWriter(Entity ent,SQLTerminal t) {
|
||||
entity=ent;
|
||||
terminal=t;
|
||||
base=(entity.getBase()!=null)?new SQLWriter(entity.getBase(),t):null;
|
||||
// we select proper fields for this entity
|
||||
FieldSlice slice=new FieldSlice(entity).including(Field.FLAG_STORABLE); // includes all even autoincrement
|
||||
while(slice.hasNext()){
|
||||
Field f=slice.next();
|
||||
Entity e=slice.nextEntity();
|
||||
if(e!=entity) continue; // skip if not part of this entity
|
||||
if(f.isAutoIncrement()){
|
||||
generated.add(f);
|
||||
}else{
|
||||
supplied.add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
public String compileInsertRecipe(){
|
||||
if(insertSQL!=null) return insertSQL;
|
||||
SQL buf=new SQL(terminal);
|
||||
buf.insert(entity,supplied);
|
||||
insertSQL=buf.toString();
|
||||
return insertSQL;
|
||||
}
|
||||
public String compileUpdateRecipe(){
|
||||
if(updateSQL!=null) return updateSQL;
|
||||
SQL buf=new SQL(terminal);
|
||||
buf.update(entity,supplied);
|
||||
updateSQL=buf.toString();
|
||||
return updateSQL;
|
||||
}
|
||||
public boolean isLinkExternal(){
|
||||
return external!=null;
|
||||
}
|
||||
public SQLWriter setExternalLink(Connection link){
|
||||
external=link;
|
||||
return this;
|
||||
}
|
||||
protected Connection getExternalLink(){
|
||||
return external;
|
||||
}
|
||||
protected Connection getInternalLink(){
|
||||
try{
|
||||
if(insertStmt!=null) return insertStmt.getConnection();
|
||||
if(updateStmt!=null) return updateStmt.getConnection();
|
||||
}catch(SQLException ex){
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public SQLWriter open() throws SQLException{
|
||||
Connection link=isLinkExternal()?getExternalLink():terminal.getConnection();
|
||||
if(base!=null) base.setExternalLink(link).open(); // definitely external link for base
|
||||
String inSql=compileInsertRecipe();
|
||||
String upSql=compileUpdateRecipe();
|
||||
//System.out.println("INS:"+inSql);
|
||||
//System.out.println("UPD:"+upSql);
|
||||
String[] genkeys=new String[generated.size()];
|
||||
for(int i=0;i<generated.size();i++){
|
||||
Field f=generated.get(i);
|
||||
genkeys[i]=f.getName();
|
||||
}
|
||||
insertStmt=link.prepareStatement(inSql,genkeys);
|
||||
updateStmt=link.prepareStatement(upSql);
|
||||
//result=prep.executeQuery();
|
||||
//if(link.getAutoCommit()==false) link.commit();
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public void close() throws IOException{
|
||||
if(base!=null) base.close(); // since link is external it will not close link just the rest
|
||||
Connection link=getInternalLink();
|
||||
if(insertStmt!=null){
|
||||
try{
|
||||
insertStmt.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
}
|
||||
if(updateStmt!=null){
|
||||
try{
|
||||
updateStmt.close();
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
}
|
||||
try{
|
||||
if(link!=null && external!=link) link.close();
|
||||
external=null;
|
||||
}catch(SQLException ex){
|
||||
if(error==null) error=ex;
|
||||
}
|
||||
if(error!=null){
|
||||
if(error instanceof IOException) throw (IOException)error;
|
||||
else throw new IOException(error);
|
||||
}
|
||||
|
||||
}
|
||||
public void flush(Iterator<DBO> items) throws SQLException {
|
||||
Connection link=isLinkExternal()?getExternalLink():getInternalLink();
|
||||
boolean autocommited=link.getAutoCommit();
|
||||
try{
|
||||
link.setAutoCommit(false);
|
||||
while(items.hasNext()){
|
||||
DBO rec=items.next();
|
||||
writeRecord(rec);
|
||||
}
|
||||
if(!link.getAutoCommit()){
|
||||
link.commit();
|
||||
}
|
||||
}catch(SQLException ex){
|
||||
if(!link.getAutoCommit()){
|
||||
link.rollback();
|
||||
}
|
||||
throw ex;
|
||||
}finally{
|
||||
link.setAutoCommit(autocommited);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This calls one update/insert. It can and is called from outside in case of nesting when link is external.
|
||||
* @param rec
|
||||
* @throws SQLException
|
||||
*/
|
||||
public boolean writeRecord(DBO rec) throws SQLException{
|
||||
if(base!=null) base.writeRecord(rec); // save the superclass first
|
||||
// select mode
|
||||
int pindex=0;
|
||||
Field pk=entity.getPk();
|
||||
boolean pk_owned=entity.isOwned(pk);
|
||||
PreparedStatement stmt=null;
|
||||
if(rec.getStatus()==DBO.Status.NEW){
|
||||
stmt=insertStmt;
|
||||
// need to inject pk here is not owned
|
||||
if(!pk_owned) stmt.setObject(++pindex,pk.get(rec,null),terminal.getTypeId(pk.getType(),pk.getTypeParams()));
|
||||
}
|
||||
if(rec.getStatus()==DBO.Status.USED){
|
||||
stmt=updateStmt;
|
||||
// update has a pk condition for sure
|
||||
stmt.setObject(supplied.size()+1,pk.get(rec,null),terminal.getTypeId(pk.getType(),pk.getTypeParams()));
|
||||
}
|
||||
if(stmt==null) return false;
|
||||
// copy values
|
||||
for(int index=0;index<supplied.size();index++){
|
||||
Field f=supplied.get(index);
|
||||
pindex+=1;
|
||||
int tid=terminal.getTypeId(f.getType(),f.getTypeParams());
|
||||
Object val=f.get(rec,null);
|
||||
//System.out.println("Param:"+pindex+":"+f.getName()+":"+val);
|
||||
stmt.setObject(pindex,val,tid);
|
||||
}
|
||||
int ucode=stmt.executeUpdate();
|
||||
//System.out.println("UCode:"+ucode);
|
||||
if(rec.getStatus()==DBO.Status.NEW){
|
||||
this.itemsInserted+=ucode;
|
||||
if(ucode>0 && !generated.isEmpty()){
|
||||
try (ResultSet keys = stmt.getGeneratedKeys()) {
|
||||
if(keys.next()){
|
||||
for(int i=0;i<generated.size();i++){
|
||||
Field f=generated.get(i);
|
||||
Object autoval=keys.getObject(i+1);
|
||||
f.set(rec,autoval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(rec.getStatus()==DBO.Status.USED){
|
||||
this.itemsUpdated+=ucode;
|
||||
}
|
||||
return ucode>0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.reliancy.dbo;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Iterator;
|
||||
/** Iterator interface suitable for dbo resultsets.
|
||||
*
|
||||
*/
|
||||
public interface SiphonIterator<T> extends Iterator<T>, Closeable {
|
||||
}
|
||||
@@ -25,6 +25,13 @@ public interface Terminal {
|
||||
public default Terminal meta(Entity ent){
|
||||
return null;
|
||||
}
|
||||
public default <T extends DBO> T load(Class<T> cls,Object...id) throws IOException {
|
||||
Entity ent=Entity.recall(cls);
|
||||
String sig="/"+ent.getName()+"/load";
|
||||
try(Action act=begin(sig).load(ent).limit(1).if_pk(id).execute()){
|
||||
return (T)act.first();
|
||||
}
|
||||
}
|
||||
public default DBO load(Entity ent,Object...id) throws IOException {
|
||||
String sig="/"+ent.getName()+"/load";
|
||||
try(Action act=begin(sig).load(ent).limit(1).if_pk(id).execute()){
|
||||
@@ -39,6 +46,7 @@ public interface Terminal {
|
||||
}
|
||||
}
|
||||
public default boolean delete(DBO rec) throws IOException {
|
||||
if(rec==null) return false;
|
||||
Entity ent=rec.getType();
|
||||
String sig="/"+ent.getName()+"/delete";
|
||||
try(Action act=begin(sig).delete(ent).setItems(rec).execute()){
|
||||
|
||||
@@ -47,8 +47,13 @@ public final class HTTP {
|
||||
public static String guess_mime(Object ret) {
|
||||
if(ret instanceof CharSequence){
|
||||
CharSequence retstr=(CharSequence)ret;
|
||||
if(retstr.length()>0 && retstr.charAt(0)=='<') return "text/html";
|
||||
if(retstr.length()>0 && "{[".indexOf(retstr.charAt(0))!=-1) return "application/json";
|
||||
for(int index=0;index<retstr.length();index++){
|
||||
char ch=retstr.charAt(index);
|
||||
if(Character.isWhitespace(ch)) continue;
|
||||
if(ch=='<') return "text/html";
|
||||
if(ch=='{' || ch=='[') return "application/json";
|
||||
break;
|
||||
}
|
||||
return "text/plain";
|
||||
}
|
||||
if(ret instanceof byte[]){
|
||||
|
||||
@@ -206,7 +206,7 @@ public class Router extends AbstractHandler{
|
||||
String ret="";
|
||||
try {
|
||||
Template.search_path("./var",SecurityPolicy.class);
|
||||
Template t=Template.find("resources/login.j2");
|
||||
Template t=Template.find("/templates/login.hbs");
|
||||
System.out.println("Template:"+t);
|
||||
ret = t.render(context).toString();
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{% extends "base.j2" %}
|
||||
{% block content %}
|
||||
<div>Hello from code: {{name}}!</div>
|
||||
{% endblock %}
|
||||
@@ -1,16 +1,23 @@
|
||||
package com.reliancy.rec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/** Base class of meta objects.
|
||||
* We use it to describe certain meta information. We derive from it Slot.
|
||||
* We define keys list of slots on the header level to describe slots.
|
||||
* We define keys list of slots on the header level to describe sub-slots.
|
||||
*
|
||||
* This class describes structure of Fields or Entities via the keys array of slots.
|
||||
* Additionally we provide a number of methods to locate, set or get or remove or add slots.
|
||||
* However slots could reside in other places such as base classes and so getOwnSlots will return a
|
||||
* bare list of slots in this object while all other methods will take into account other sources.
|
||||
* We do this to stay consistent at Rec level when Hdr inheritance comes into play.
|
||||
*/
|
||||
public class Hdr {
|
||||
public static final int FLAG_ARRAY =0x0001;
|
||||
public static final int FLAG_CHANGED =0x0002;
|
||||
public static final int FLAG_HIDDEN =0x0004;
|
||||
public static final int FLAG_STORABLE =0x0004;
|
||||
public static final int FLAG_LOCKED =0x0008;
|
||||
int flags;
|
||||
String name;
|
||||
@@ -30,8 +37,11 @@ public class Hdr {
|
||||
@Override
|
||||
public String toString(){
|
||||
StringBuilder ret=new StringBuilder();
|
||||
ret.append("{").append("flags:").append(flags).append(",name:").append(name);
|
||||
ret.append(",dim:").append(keys.size()).append("}");
|
||||
ret.append(name).append(":");
|
||||
ret.append("{")
|
||||
.append("flags:").append(flags)
|
||||
.append(",dim:").append(count())
|
||||
.append("}");
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
@@ -53,6 +63,9 @@ public class Hdr {
|
||||
public void setType(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
public int getFlags(){
|
||||
return flags;
|
||||
}
|
||||
public Hdr raiseFlags(int f){
|
||||
flags|=f;
|
||||
return this;
|
||||
@@ -67,36 +80,51 @@ public class Hdr {
|
||||
public <T extends Hdr> T castAs(Class<T> clazz){
|
||||
return clazz.cast(this);
|
||||
}
|
||||
public int findSlot(String name){
|
||||
return findSlot(name,0);
|
||||
public List<Slot> getOwnSlots(){
|
||||
return keys;
|
||||
}
|
||||
public int findSlot(String name,int ofs){
|
||||
ListIterator<Slot> it=keys.listIterator(ofs);
|
||||
public boolean isOwned(Slot s){
|
||||
return keys.contains(s);
|
||||
}
|
||||
public Iterator<Slot> iterator(int offset){
|
||||
return keys.listIterator(offset);
|
||||
}
|
||||
public int indexOf(String name){
|
||||
return indexOf(name,0);
|
||||
}
|
||||
public int indexOf(String name,int ofs){
|
||||
Iterator<Slot> it=iterator(ofs);
|
||||
int index=-1;
|
||||
while(it.hasNext()){
|
||||
int index=it.nextIndex();
|
||||
index+=1;
|
||||
Slot e=it.next();
|
||||
if(e.getName().equalsIgnoreCase(name)) return index;
|
||||
//if(e.getName().equalsIgnoreCase(name)) return index;
|
||||
if(e.equals(name)) return index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public int findSlot(Slot s,int ofs){
|
||||
ListIterator<Slot> it=keys.listIterator(ofs);
|
||||
public int indexOf(Slot s,int ofs){
|
||||
Iterator<Slot> it=iterator(ofs);
|
||||
int index=-1;
|
||||
while(it.hasNext()){
|
||||
int index=it.nextIndex();
|
||||
index+=1;
|
||||
Slot e=it.next();
|
||||
if(e==s) return index;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public Slot makeSlot(String name){
|
||||
return new Slot(name);
|
||||
}
|
||||
/**
|
||||
* this version will get or create a slot by given name.
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public Slot getSlot(String name){
|
||||
int index=findSlot(name);
|
||||
public Slot getSlot(String name,boolean make){
|
||||
int index=indexOf(name);
|
||||
if(index<0){
|
||||
return new Slot(name);
|
||||
return make?makeSlot(name):null;
|
||||
}else{
|
||||
return getSlot(index);
|
||||
}
|
||||
@@ -116,11 +144,7 @@ public class Hdr {
|
||||
keys.set(index,s);
|
||||
return this;
|
||||
}
|
||||
public Slot[] slots(Slot... slots){
|
||||
if(slots!=null && slots.length>0){
|
||||
keys.clear();
|
||||
for(int i=0;i<slots.length;i++) keys.add(slots[i]);
|
||||
}
|
||||
return keys.toArray(new Slot[keys.size()]);
|
||||
public int count(){
|
||||
return keys.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,13 @@ public class JSON {
|
||||
public static final void writes(Rec rec,Appendable sink) throws IOException{
|
||||
JSONEncoder.encode(rec, sink);
|
||||
}
|
||||
|
||||
public static final String toString(Rec rec){
|
||||
StringBuffer buf=new StringBuffer();
|
||||
try {
|
||||
writes(rec,buf);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class Obj implements Rec{
|
||||
if(s==null) throw new IllegalArgumentException("invalid key provided");
|
||||
if(isArray()) throw new IllegalStateException("array not mappable with:"+s.getName());
|
||||
int index=s.getPosition(); // try slot position
|
||||
if(index<0) index=meta.findSlot(s.getName());// fall back to search if slot not set
|
||||
if(index<0) index=meta.indexOf(s.getName());// fall back to search if slot not set
|
||||
if(index<0){
|
||||
values.add(val);
|
||||
meta.addSlot(s);
|
||||
@@ -138,14 +138,14 @@ public class Obj implements Rec{
|
||||
if(s==null) throw new IllegalArgumentException("invalid key provided");
|
||||
//if(keys==null) throw new IllegalStateException("array not mappable with:"+s.getName());
|
||||
int index=s.getPosition(); // try slot position
|
||||
if(index<0 && !isArray()) index=meta.findSlot(s.getName());// fall back to search if slot not set
|
||||
if(index<0 && !isArray()) index=meta.indexOf(s.getName());// fall back to search if slot not set
|
||||
return index<0?def:values.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rec remove(Slot s) {
|
||||
int index=s.getPosition(); // try slot position
|
||||
if(index<0 && !isArray()) index=meta.findSlot(s.getName());// fall back to search if slot not set
|
||||
if(index<0 && !isArray()) index=meta.indexOf(s.getName());// fall back to search if slot not set
|
||||
if(index>=0) remove(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public interface Rec extends Vec{
|
||||
public Rec remove(Slot s);
|
||||
public default Slot getSlot(String name){
|
||||
Hdr m=meta();
|
||||
return m!=null?m.getSlot(name):null;
|
||||
return m!=null?m.getSlot(name,true):null;
|
||||
}
|
||||
public default Slot getSlot(int pos){
|
||||
Hdr m=meta();
|
||||
|
||||
@@ -11,7 +11,7 @@ public class Slot extends Hdr {
|
||||
Object getInitalValue(Slot s,Rec rec);
|
||||
}
|
||||
public static final Initializer DEFAULT_INITIALIZER=new Initializer(){
|
||||
public Object getInitalValue(Slot s,Rec rec) {return s.getDefaultValue();}
|
||||
public Object getInitalValue(Slot s,Rec rec) {return s.getInitValue();}
|
||||
};
|
||||
int position;
|
||||
Object defaultValue;
|
||||
@@ -25,23 +25,29 @@ public class Slot extends Hdr {
|
||||
this.position=-1;
|
||||
this.initValue=DEFAULT_INITIALIZER;
|
||||
}
|
||||
public boolean equals(String str){
|
||||
return name.equalsIgnoreCase(str);
|
||||
}
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
public void setPosition(int position) {
|
||||
public Slot setPosition(int position) {
|
||||
this.position = position;
|
||||
return this;
|
||||
}
|
||||
public Object getDefaultValue() {
|
||||
public Object getInitValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
public Slot setInitValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
public Initializer getInitValue() {
|
||||
public Initializer getInitVia() {
|
||||
return initValue;
|
||||
}
|
||||
public void setInitValue(Initializer initValue) {
|
||||
public Slot setInitVia(Initializer initValue) {
|
||||
this.initValue = initValue;
|
||||
return this;
|
||||
}
|
||||
public int toString(Object val, StringBuilder buf) {
|
||||
int length0=buf.length();
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.reliancy.util;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Iterator;
|
||||
|
||||
public interface CloseableIterator<T> extends Iterator<T>, Closeable {
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public final class Handy {
|
||||
if(clazz.isAssignableFrom(val.getClass())) return clazz; // we are assignable
|
||||
if(val instanceof String){
|
||||
String value=(String) val;
|
||||
if(value.isBlank() || value.equals("''") || value.equals("\"\"")) return null;
|
||||
if(value.isEmpty() || value.equals("''") || value.equals("\"\"")) return null;
|
||||
if( Boolean.class==( clazz ) || boolean.class==( clazz ) ) return Boolean.parseBoolean( value );
|
||||
if( Byte.class==( clazz ) || byte.class==( clazz ) ) return Byte.parseByte( value );
|
||||
if( Short.class==( clazz ) || short.class==( clazz ) ) return Short.parseShort( value );
|
||||
@@ -454,5 +454,31 @@ public final class Handy {
|
||||
all.toArray(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static String toString(Object...args){
|
||||
StringBuilder buf=new StringBuilder();
|
||||
if(args.length>1){
|
||||
buf.append("[");
|
||||
for(int i=0;i<args.length;i++) buf.append(i==0?"":",").append(toString(args[i]));
|
||||
buf.append("]");
|
||||
}else if(args.length==1){
|
||||
Object arg=args[0];
|
||||
if(arg instanceof Iterable){
|
||||
java.util.Iterator<?> it=((Iterable<?>)arg).iterator();
|
||||
buf.append("[");
|
||||
while(it.hasNext()) buf.append(buf.length()>1?",":"").append(it.next());
|
||||
buf.append("]");
|
||||
}else
|
||||
if(arg instanceof java.util.Map){
|
||||
java.util.Map<?,?> marg=(Map<?,?>) arg;
|
||||
buf.append("{");
|
||||
for(java.util.Map.Entry<?,?>e:marg.entrySet()){
|
||||
buf.append(e.getKey().toString()).append(":").append(toString(e.getValue()));
|
||||
}
|
||||
buf.append("}");
|
||||
}else{
|
||||
buf.append(String.valueOf(arg));
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,28 @@
|
||||
package com.reliancy.util;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.github.jknack.handlebars.Handlebars;
|
||||
import com.github.jknack.handlebars.io.AbstractTemplateLoader;
|
||||
import com.github.jknack.handlebars.io.TemplateSource;
|
||||
import com.github.jknack.handlebars.io.URLTemplateSource;
|
||||
|
||||
/*
|
||||
import com.hubspot.jinjava.Jinjava;
|
||||
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
|
||||
import com.hubspot.jinjava.loader.ResourceLocator;
|
||||
*/
|
||||
|
||||
/**
|
||||
* We will manage template rendering thru this class.
|
||||
*/
|
||||
public class Template {
|
||||
/*
|
||||
static Jinjava jinjava;
|
||||
static{
|
||||
jinjava = new Jinjava();
|
||||
@@ -32,6 +40,27 @@ public class Template {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
public static class HBLoader extends AbstractTemplateLoader{
|
||||
public HBLoader(){
|
||||
this.setPrefix("/templates/");
|
||||
}
|
||||
@Override
|
||||
public TemplateSource sourceAt(String location) throws IOException {
|
||||
String fullpath=this.resolve(location);
|
||||
URL loc=Resources.findFirst(null,fullpath,Template.search_path);
|
||||
System.out.println(location+":"+loc+":"+fullpath);
|
||||
if (loc == null) {
|
||||
Logger.getLogger(Template.class.getSimpleName()).warning("Missing template"+fullpath);
|
||||
throw new FileNotFoundException(location);
|
||||
}
|
||||
return new URLTemplateSource(location,loc);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
static Handlebars handlebars = new Handlebars(new HBLoader());
|
||||
|
||||
static Object[] search_path;
|
||||
static HashMap<String,Template> cache=new HashMap<>();
|
||||
/** renders a template to string, possibly locates it first.
|
||||
@@ -56,7 +85,6 @@ public class Template {
|
||||
Template ret=cache.get(path);
|
||||
if(ret!=null) return ret;
|
||||
URL loc=Resources.findFirst(null, path, (sp!=null && sp.length>0?sp:search_path));
|
||||
System.out.println("TLOCL:"+loc);
|
||||
if(loc==null) return null;
|
||||
ret=new Template(loc);
|
||||
cache.put(path,ret);
|
||||
@@ -66,7 +94,7 @@ public class Template {
|
||||
if(sp!=null && sp.length>0) search_path=sp;
|
||||
return search_path;
|
||||
}
|
||||
|
||||
com.github.jknack.handlebars.Template recipe;
|
||||
final URL location;
|
||||
String source;
|
||||
public Template(URL location){
|
||||
@@ -88,7 +116,11 @@ public class Template {
|
||||
}
|
||||
public CharSequence render(Map<String,?> context) throws IOException{
|
||||
if(source==null) load();
|
||||
String ret = jinjava.render(source, context);
|
||||
//String ret = jinjava.render(source, context);
|
||||
if(recipe==null){
|
||||
recipe=handlebars.compileInline(source);
|
||||
}
|
||||
String ret=recipe.apply(context);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{{#partial "content"}}
|
||||
Calling base
|
||||
{{/partial}}
|
||||
{{> base}}
|
||||
@@ -4,25 +4,67 @@ import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
import com.reliancy.rec.JSON;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
public class TerminalTest {
|
||||
@Entity.Info(
|
||||
name="dbo.Maps"
|
||||
)
|
||||
public static class Maps extends DBO{
|
||||
public static Field map_id=new Field("Map_id",Integer.class);
|
||||
public static Field map_name=new Field("Map_name",String.class);
|
||||
public static Field map_id=Field.Int("Map_id").setPk(true);
|
||||
public static Field map_name=Field.Str("Map_name");
|
||||
public static Field created=Field.DateTime("Created");
|
||||
public static Field active=Field.Bool("Active");
|
||||
static{
|
||||
Entity.publish(Maps.class);
|
||||
//Entity.publish(Maps.class);
|
||||
}
|
||||
}
|
||||
@Entity.Info(
|
||||
name="public.securable"
|
||||
)
|
||||
public static class Securable extends DBO{
|
||||
public static Field id=Field.Int("id").setPk(true).setAutoIncrement(true);
|
||||
public static Field kind=Field.Str("kind");
|
||||
public static Field name=Field.Str("name");
|
||||
public static Field display_name=Field.Str("display_name");
|
||||
public static Field created=Field.DateTime("created_on");
|
||||
public static Field is_essential=Field.Bool("is_essential");
|
||||
static{
|
||||
//Entity.publish(Maps.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Entity.Info(
|
||||
name="public.product"
|
||||
)
|
||||
public static class Product extends Securable{
|
||||
public static Field valid_since=Field.DateTime("valid_since");
|
||||
public static Field valid_until=Field.DateTime("valid_until");
|
||||
public static Field short_info=Field.Str("short_info");
|
||||
|
||||
}
|
||||
|
||||
static SQLTerminal t;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAllTestMethods() {
|
||||
System.out.println("Invoked once before all test methods");
|
||||
String url="jdbc:postgresql://postgres:Ramudin99@bigbang:5432/Test";
|
||||
t=new SQLTerminal(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plain CRUD
|
||||
* jdbc connectivity
|
||||
* @throws IOException
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Test
|
||||
public void connection() throws IOException, SQLException{
|
||||
String url="jdbc:postgresql://postgres:Ramudin99@bigbang:5432/Test";
|
||||
SQLTerminal t=new SQLTerminal(url);
|
||||
try(Connection c=t.getConnection()){
|
||||
System.out.println("Connection:"+c);
|
||||
try (Statement stmt = c.createStatement()) {
|
||||
@@ -38,5 +80,38 @@ public class TerminalTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void simpleCRUD() throws IOException, SQLException{
|
||||
System.out.println("SimpleCRUD");
|
||||
try(Action act=t.begin().load(Maps.class).execute()){
|
||||
for(DBO o:act){
|
||||
System.out.println("DBO:"+o);
|
||||
}
|
||||
}
|
||||
Entity.retract(Maps.class);
|
||||
}
|
||||
@Test
|
||||
public void complexCRUD() throws IOException, SQLException{
|
||||
System.out.println("ComplexCRUD");
|
||||
try(Action act=t.begin().load(Product.class).execute()){
|
||||
for(DBO o:act){
|
||||
System.out.println("DBO:"+o);
|
||||
}
|
||||
}
|
||||
Product p=new Product();
|
||||
p.setStatus(DBO.Status.USED);
|
||||
Product.kind.set(p,Product.class.getSimpleName());
|
||||
Product.name.set(p,"myproduct");
|
||||
Product.created.set(p,new Date());
|
||||
Product.short_info.set(p,"a sweet melody");
|
||||
Product.display_name.set(p,"first entry");
|
||||
System.out.println("P0:"+JSON.toString(p));
|
||||
t.save(p);
|
||||
System.out.println("P1:"+JSON.toString(p));
|
||||
Product pp=t.load(Product.class, 35);
|
||||
System.out.println("Returning:"+pp);
|
||||
//t.delete(pp);
|
||||
Entity.retract(Maps.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Flask Template Example</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<style type="text/css">
|
||||
.container {
|
||||
max-width: 500px;
|
||||
padding-top: 100px;
|
||||
}
|
||||
h2 {color: red;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>This is part of my base template</h2>
|
||||
<br>
|
||||
{% block content %}{% endblock %}
|
||||
<br>
|
||||
<h2>This is part of my base template</h2>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user