├── .gitignore
├── LICENSE
├── ObjectiveSync.json
├── README.md
├── build.gradle
├── classes
└── ch
│ └── loway
│ └── oss
│ └── ObjectiveSync
│ ├── CS.java
│ ├── Column.java
│ ├── JdbcPattern.java
│ ├── ObjectiveFetch.java
│ ├── SqlTools.java
│ ├── table
│ ├── SqlField.java
│ ├── SqlTable.java
│ └── type
│ │ ├── FuncValue.java
│ │ ├── IntValue.java
│ │ ├── StringValue.java
│ │ └── TableValue.java
│ └── updater
│ ├── FieldSet.java
│ ├── SqlFieldVal.java
│ ├── SqlUpdater.java
│ └── deferred
│ ├── DeferredLoadByParent.java
│ └── DeferredLoader.java
└── tests
└── ch
└── loway
└── oss
└── ObjectiveSync
├── CSTest.java
├── JdbcPatternTest.java
├── ObjectiveFetchTest.java
├── ObjectiveFetch_1N_Test.java
├── ObjectiveFetch_ReadOnlyTest.java
├── learnH2
└── LearnH2Test.java
├── maps
├── ACInfo.java
├── ACInfoDB.java
├── Organization.java
├── OrganizationDB.java
├── Person.java
└── PersonDB.java
└── table
└── SqlTableTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Package Files
4 | *.jar
5 | *.war
6 | *.ear
7 |
8 | # gradle
9 | build/
10 | .gradle/
11 |
12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
13 | hs_err_pid*
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/ObjectiveSync.json:
--------------------------------------------------------------------------------
1 | {
2 | "group": "loway",
3 | "artifact": "ObjectiveSync",
4 | "license": "Loway - All rights reserved",
5 | "depends": [
6 | "org.slf4j:slf4j-api:1.7.7"
7 | ],
8 | "testdepends": [
9 | ]
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ObjectiveSync
2 | =============
3 |
4 | A thin Java object persistence layer for JDBC.
5 |
6 |
7 | **Downloading**
8 |
9 | This library used to be on JCenter. Frankly, I cannot be bothered to jump through all the hoops to publish on Maven Central - I do this in my spare time, give me a break. So you can build it on your own and publish on your private repo - I enclose a recipe for PomFrites https://github.com/l3nz/pom_frites to make it easier. Enjoy.
10 |
11 |
12 |
13 |
14 |
15 |
16 | News:
17 |
18 | * Feb 21, 2015: Version 0.1.4 will have a working Connection.
19 |
20 | Ideas
21 | -----
22 |
23 | Great things in Hibernate:
24 |
25 | * Association mapping: loading and saving associations in one easy step
26 | * Database independence
27 |
28 | Not-so-great things:
29 |
30 | * What you get from Hibernate is not POJOs. Need for DAOs and copy, copy, copy. This is useless code and often breaks.
31 | * Configuration too complex. You end up modelling everything around what works in Hibernate, not your classes (as promised)
32 | or what works in the database (that is the real constraint you are facing)
33 | * Hibernate-aware code everywhere
34 | * Different query language without a reasonable shell - you have to write code to test out a query
35 | * Very complex and inefficient queries triggered for no apparent reason. You spend a day to control something
36 | that would have taken a few minutes in SQL
37 | * Slow to start - big problem for testing.
38 | * Bad practice - if you hide the database, you may get something done quickly, but it's a bad idea.
39 | If yor Java code expects to have a collection of one million objects as an array, it does not matter
40 | if they are lazily loaded or not - some code somewhere might want to iterate over them, and this will
41 | kill the process. You cannot really forget that there is a database somewhere, and you should not do it.
42 | * Aborts on commit. For long-lived transaction, you never know WHAT made the transaction abort. And what can you do next?
43 |
44 | Aims
45 | ----
46 |
47 | * Minimal wrapper over JDBC.
48 | * Querying done in SQL. You should not be afraid of SQL. If you are, you should not be doing anything above the trivial CRUD.
49 | * Centralizing object marshaling and unmarshaling - each object should know how to sync itself and its descendents
50 | * Single syntax for inserting and updating
51 | * Ruby-like objectivized JDBC fetching with exception handling
52 | * User-definable deep fetching and updating (almost Hibernate-like).
53 | * Batch API to avoid round-trips when submitting multiple queries.
54 | * Stats collection and similar stuff.
55 |
56 | Downloading
57 | -----------
58 |
59 | If you use Gradle (or any tool using Maven dependencies) you can simply declare the lib as:
60 |
61 |
62 | repositories {
63 | mavenCentral()
64 | mavenRepo(url: 'http://jcenter.bintray.com')
65 | }
66 |
67 |
68 | dependencies {
69 | compile 'ch.loway.oss.ObjectiveSync:ObjectiveSync:0.1.4'
70 | }
71 |
72 |
73 | This will download the package and all required dependencies (that is basically only the logging framework).
74 |
75 |
76 | Getting started
77 | ---------------
78 |
79 | You can see examples of simple operations under the 'tests/' folder.
80 |
81 | * Object are POJOs. No need for importing interfaces, annotations, etc. so they are easy to move to the client side using
82 | GWT or Jackson.
83 | * For each object, you create a database accessor class, usually called "ObjectDB". This class extends ObjectiveFetch.
84 | The ones we use for testing are in "ch.loway.oss.ObjectiveSync.maps".
85 |
86 | The database accessor requires:
87 |
88 | * a **table()** method that specifies the fields for your table
89 | * a **load()** method that given a recordset row will build you an object and will schedule DeferredLoading for
90 | additional objects. So for example, in the Organization class you load all organizations and schedule
91 | deferred loading of all Persons who belong to them. This means you avoid the m*n problem when joining tables
92 | and that in the future we can schedule deferred loading to be efficient (e.g. batch, or in case you are loading
93 | the same record multiple times)
94 | * a **save()** method that will save your object as a row on the DB. If you do not need to save, don't implement it.
95 | Columns have valid defaults for insert and update.
96 | * a **saveSubObjects()** that will save dependent objects.
97 | * an **updatePrimaryKey()** that will be triggered on inserts.
98 |
99 | How it all works
100 |
101 | * PKs are strings (or numbers) and are created by the database layer. No composite keys supported.
102 | * When loading objects, you can either create full queries (for aggregation queries, e.g. avg() count() or whatever())
103 | or pass only the "WHERE ... GROUP BY ... ORDER BY ...." clauses - the object knows its own fields.
104 | * Whatever goes wrong raises an SQLException
105 | * The library is meant to be easy to use and to replace Hibernate in simple cases. When Java and the DB think
106 | different, the database wins.
107 |
108 | We have a group for ObjectiveSync on G+ - https://plus.google.com/u/0/communities/108551537631226271215 - come and say hi.
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // build file here
2 |
3 |
4 | apply plugin: 'java'
5 | apply plugin: 'findbugs'
6 | apply plugin: 'maven'
7 | def env = System.getenv()
8 |
9 | //
10 | // I defaults valgono per HUDSON
11 | //
12 |
13 | project.ext {
14 | webapp_name = 'ObjectiveSync'
15 | app_version = '0.1.4'
16 | build_number = env["BUILD_NUMBER"]
17 | version_class = 'ch/loway/oss/ObjectiveSync/CS.java'
18 | build_time = "" + new Date()
19 | }
20 |
21 |
22 |
23 | repositories {
24 | mavenCentral()
25 | }
26 |
27 | configurations {
28 | ObjSync
29 | }
30 |
31 |
32 |
33 | dependencies {
34 | ObjSync( 'mysql:mysql-connector-java:5.1.30' )
35 | ObjSync( 'org.slf4j:slf4j-jdk14:1.7.7' )
36 | ObjSync( 'com.h2database:h2:1.4.178' )
37 | }
38 |
39 |
40 | dependencies {
41 | compile 'org.slf4j:slf4j-api:1.7.7'
42 |
43 | testCompile 'junit:junit:4.10'
44 | testCompile 'org.slf4j:slf4j-jdk14:1.7.7'
45 | testCompile 'mysql:mysql-connector-java:5.1.30'
46 | testCompile 'com.h2database:h2:1.4.178'
47 |
48 | }
49 |
50 |
51 |
52 | task solveDeps << {
53 |
54 | copy {
55 | from configurations.ObjSync
56 | into "$buildDir/dependencies"
57 | include '**/*.jar'
58 | }
59 |
60 | }
61 |
62 |
63 |
64 |
65 |
66 | sourceSets {
67 |
68 | main {
69 | java {
70 | srcDirs "$buildDir/classes_subst"
71 | }
72 | }
73 |
74 | test {
75 | java {
76 | srcDirs "./tests"
77 | }
78 | }
79 | }
80 |
81 |
82 | task writeNewPom << {
83 | pom {
84 | project {
85 | groupId 'ch.loway.oss.ObjectiveSync'
86 | artifactId 'ObjectiveSync'
87 | version project.ext.app_version
88 | inceptionYear '2014'
89 | licenses {
90 | license {
91 | name 'GNU LGPL v3'
92 | }
93 | }
94 | }
95 | }.writeTo("$buildDir/libs/ObjectiveSync-" + project.ext.app_version + ".pom")
96 | }
97 |
98 |
99 |
100 | task sourcesJar(type: Jar, dependsOn: classes) {
101 | classifier = 'sources'
102 | from "$buildDir/classes_subst"
103 | archiveName project.ext.webapp_name + "-" + project.ext.app_version + "-sources.jar"
104 | }
105 |
106 | task javadocJar(type: Jar, dependsOn: javadoc) {
107 | classifier = 'javadoc'
108 | from javadoc.destinationDir
109 | archiveName project.ext.webapp_name + "-" + project.ext.app_version + "-javadoc.jar"
110 | }
111 |
112 |
113 |
114 | jar {
115 | manifest {
116 | attributes("Implementation-Title": project.ext.webapp_name,
117 | "Implementation-Version": project.ext.app_version)
118 | }
119 | archiveName project.ext.webapp_name + "-" + project.ext.app_version + ".jar"
120 | from( "$buildDir/classes_subst" )
121 | }
122 | jar.dependsOn writeNewPom
123 | jar.dependsOn sourcesJar
124 | jar.dependsOn javadocJar
125 |
126 |
127 |
128 | task setVersionInSources() {
129 | doLast {
130 | def cjd = new File( "$buildDir/classes_subst" )
131 | cjd.mkdirs();
132 |
133 | def FileTree tree = fileTree( dir: "./classes" )
134 | //tree.each { File file -> println file }
135 |
136 | copy {
137 | from './classes'
138 | into "$buildDir/classes_subst"
139 | }
140 |
141 | ant.replace( file: "$buildDir/classes_subst/" + project.ext.version_class,
142 | token: "VERSION",
143 | value: "VERSION = \"" + project.ext.app_version + "\"; //"
144 | )
145 |
146 | ant.replace( file: "$buildDir/classes_subst/" + project.ext.version_class,
147 | token: "BUILD_N",
148 | value: "BUILD_N = \"" + project.ext.build_number + " - " + project.ext.build_time + "\"; //"
149 | )
150 | }
151 | }
152 | compileJava.dependsOn setVersionInSources
153 |
154 | // decide what to see -only one can be enabled
155 | // get the HTML report from build/reports/findbugs/main.html
156 | tasks.withType(FindBugs) {
157 | reports {
158 | xml.enabled = false
159 | html.enabled = true
160 | }
161 | ignoreFailures = true
162 | }
163 |
164 | // Force sources to 1.5
165 | compileJava {
166 | sourceCompatibility="1.5"
167 | targetCompatibility="1.5"
168 | }
169 |
170 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/CS.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | /**
5 | *
6 | *
7 | * @author lenz
8 | */
9 | public class CS {
10 |
11 | public static final String VERSION = "1234";
12 |
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/Column.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | /**
5 | *
6 | **/
7 | public class Column {
8 |
9 |
10 | }
11 |
12 | // $Log$
13 | //
14 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/JdbcPattern.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync;
2 |
3 | import java.sql.Connection;
4 | import java.sql.ResultSet;
5 | import java.sql.SQLException;
6 | import java.sql.Statement;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * This class encodes the correct JDBC handling pattern, with all resource
12 | * reclamation and try/catch/finally logic.
13 | *
14 | * Plus, we offer a few ready-made
15 | * static methods for "vanilla" insert, update and exec.
16 | *
17 | * Note that there is no "vanilla" select as this is something you really
18 | * should customize for general usage.
19 | *
20 | *
21 | * @author lenz
22 | */
23 | public abstract class JdbcPattern {
24 |
25 | // logger is public so children can use it
26 | public static final Logger logger = LoggerFactory.getLogger(JdbcPattern.class);
27 | public Statement stmt = null;
28 | public ResultSet rs = null;
29 | int nRowsUpdated = 0;
30 | String insertKey = "";
31 | int durationMs = 0;
32 |
33 | /**
34 | * Here we perform JDBC access logic.
35 | *
36 | * Tip: the public Statement stmt and ResultSet rs are
37 | * closed automatically if defined. So you should use them
38 | * to avoid wrapping your own code in another try/catch/finally block.
39 | *
40 | * Let any errors bubble up as SQLExceptions.
41 | *
42 | * @param conn
43 | * @throws SQLException
44 | */
45 |
46 | public abstract void performJdbcAccess(Connection conn) throws SQLException;
47 |
48 |
49 | /**
50 | * This methods performs database fetching.
51 | *
52 | * @param conn
53 | * @throws SQLException
54 | */
55 |
56 | public void run(Connection conn) throws SQLException {
57 |
58 | try {
59 |
60 | long startTime = System.currentTimeMillis();
61 | performJdbcAccess(conn);
62 | durationMs = (int) (System.currentTimeMillis()-startTime);
63 |
64 | } catch (SQLException sqlEx) {
65 | logger.error("Exception when running: ", sqlEx);
66 | throw sqlEx;
67 | } finally {
68 |
69 | try {
70 | // Close the result set, statement and the connection
71 | if (rs != null) {
72 | rs.close();
73 | }
74 | if (stmt != null) {
75 | stmt.close();
76 | }
77 | } catch (Exception e) {
78 | logger.error("Ouch! Exception in finally block!", e);
79 | }
80 | }
81 |
82 | }
83 |
84 | /**
85 | * Ready-made insert updater.
86 | *
87 | * @param conn
88 | * @param insertQuery
89 | * @return the object, so you can fetch the insert-id.
90 | * @throws SQLException
91 | */
92 |
93 | public static JdbcPattern insert(Connection conn, final String insertQuery) throws SQLException {
94 | JdbcPattern pInsert = new JdbcPattern() {
95 |
96 | @Override
97 | public void performJdbcAccess(Connection conn) throws SQLException {
98 | stmt = conn.createStatement();
99 | nRowsUpdated = stmt.executeUpdate(insertQuery, Statement.RETURN_GENERATED_KEYS);
100 | rs = stmt.getGeneratedKeys();
101 |
102 | if (rs != null) {
103 | while (rs.next()) {
104 | insertKey = rs.getString(1);
105 | }
106 | }
107 | }
108 | };
109 | pInsert.run(conn);
110 | return pInsert;
111 | }
112 |
113 | /**
114 | * Ready-made update query.
115 | * This method raises an exception if the number of updater rows is != 1
116 | *
117 | * @param conn
118 | * @param updateSql
119 | * @return the object after running the query.
120 | * @throws SQLException
121 | */
122 |
123 | public static JdbcPattern update(Connection conn, final String updateSql) throws SQLException {
124 |
125 | JdbcPattern pUpdate = new JdbcPattern() {
126 |
127 | @Override
128 | public void performJdbcAccess(Connection conn) throws SQLException {
129 |
130 | stmt = conn.createStatement();
131 |
132 | // Execute the run
133 | nRowsUpdated = stmt.executeUpdate(updateSql);
134 |
135 | if (nRowsUpdated != 1) {
136 | throw new SQLException("Update failed. Rows returned: " + nRowsUpdated);
137 | }
138 | }
139 | };
140 | pUpdate.run(conn);
141 | return pUpdate;
142 |
143 | }
144 |
145 | /**
146 | * Runs a generic, no-reply query.
147 | *
148 | * @param conn
149 | * @param anySql
150 | * @return the object after running the query.
151 | * @throws SQLException
152 | */
153 |
154 | public static JdbcPattern exec(Connection conn, final String anySql) throws SQLException {
155 |
156 | JdbcPattern pExec = new JdbcPattern() {
157 |
158 | @Override
159 | public void performJdbcAccess(Connection conn) throws SQLException {
160 |
161 | stmt = conn.createStatement();
162 |
163 | // Execute the run
164 | stmt.execute(anySql);
165 |
166 | }
167 | };
168 | pExec.run(conn);
169 | return pExec;
170 |
171 | }
172 |
173 | /**
174 | * A quick-and-dirty way to run a set of statement, e.g. to create tables.
175 | *
176 | *
177 | * @param conn
178 | * @param statements
179 | * @throws SQLException
180 | */
181 |
182 | public static void execAll( Connection conn, String[] statements ) throws SQLException {
183 | for ( String statement: statements ) {
184 | exec(conn, statement);
185 | }
186 | }
187 |
188 | }
189 |
190 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/ObjectiveFetch.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync;
2 |
3 | import ch.loway.oss.ObjectiveSync.table.SqlField;
4 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
5 | import ch.loway.oss.ObjectiveSync.table.type.StringValue;
6 | import ch.loway.oss.ObjectiveSync.updater.deferred.DeferredLoader;
7 | import ch.loway.oss.ObjectiveSync.updater.FieldSet;
8 | import ch.loway.oss.ObjectiveSync.updater.SqlUpdater;
9 | import java.sql.Connection;
10 | import java.sql.ResultSet;
11 | import java.sql.SQLException;
12 | import java.util.ArrayList;
13 | import java.util.Arrays;
14 | import java.util.Collection;
15 | import java.util.Collections;
16 | import java.util.HashSet;
17 | import java.util.List;
18 | import java.util.Set;
19 | import org.slf4j.Logger;
20 | import org.slf4j.LoggerFactory;
21 |
22 | /**
23 | *
24 | *
25 | * @author lenz
26 | */
27 | public abstract class ObjectiveFetch {
28 |
29 | private final static Logger logger = LoggerFactory.getLogger(ObjectiveFetch.class);
30 |
31 | List deferredLoaders = new ArrayList();
32 |
33 | /**
34 | * Defines a table.
35 | * You need this if you are going to use the query() menthod and do
36 | * reads and updates.
37 | * If you just need reading, you can skip it all and use queryDirect()
38 | * passing the fields yourself.
39 | *
40 | * @return
41 | */
42 |
43 | public SqlTable table() {
44 | return null;
45 | }
46 |
47 | /**
48 | * Builds an element out of your row data.
49 | * The element is appened to the list of results, until you pass "null"
50 | * to signify you don't want it appended (this can be useful e.g. if
51 | * you are loading objects with sub-objects as one single join).
52 | *
53 | * @param rs
54 | * @return The list of objects.
55 | * @throws SQLException
56 | */
57 |
58 | public abstract T load(ResultSet rs) throws SQLException;
59 |
60 | /**
61 | * Saves an object to the database.
62 | * This method is not abstract as you may well have objects that
63 | * you never want to save - e.g. results of aggregation queries.
64 | *
65 | * @param obj The object to be saved
66 | * @param su The field-set of the table, where yuoi will bind data.
67 | * @throws SQLException
68 | */
69 |
70 | public void save(T obj, FieldSet su) throws SQLException {
71 | throw new IllegalArgumentException("Method save() undefined for " + obj.getClass());
72 | }
73 |
74 | /**
75 | * Saves sub-objects, if that's needed.
76 | *
77 | * @param conn
78 | * @param obj my main object
79 | * @throws SQLException
80 | */
81 |
82 | public void saveSubObjects( Connection conn, T obj ) throws SQLException {
83 |
84 | }
85 |
86 | /**
87 | * After an insert, this call-back is hit to updfate your object with the
88 | * new PK value.
89 | * You choose wheter you want o use this or not.
90 | *
91 | * @param pkFromDb The PK assigned by your DB.
92 | */
93 |
94 | public void updatePrimaryKey( T object, String pkFromDb ) {
95 | // Override me
96 | logger.debug( "PK read from DB is {} but I'm not storing it", pkFromDb );
97 | }
98 |
99 | /**
100 | * Adds a deferredLoader.
101 | *
102 | * @param l
103 | */
104 |
105 | public void deferLoading( DeferredLoader l ) {
106 | deferredLoaders.add(l);
107 | }
108 |
109 |
110 | /**
111 | * Returns the result of a query.
112 | * In this case you have to pass a query like
113 | * SELECT ... FROM ... JOIN ... WHERE ....
114 | *
115 | * In general you will want to use the query() method
116 | * that takes care of most things for you.
117 | *
118 | * @see query()
119 | * @param conn
120 | * @param sql
121 | * @return
122 | * @throws SQLException
123 | */
124 |
125 | public List queryDirect(Connection conn, final String sql) throws SQLException {
126 |
127 | final List results = new ArrayList();
128 | deferredLoaders.clear();
129 |
130 | new JdbcPattern() {
131 |
132 | @Override
133 | public void performJdbcAccess(Connection conn) throws SQLException {
134 | stmt = conn.createStatement();
135 |
136 | // Execute the run
137 | rs = stmt.executeQuery(sql);
138 |
139 | // Loop through the result set
140 | while (rs.next()) {
141 | T result = load(rs);
142 | if ( result != null ) {
143 | results.add( result );
144 | }
145 | }
146 | }
147 | }.run(conn);
148 |
149 | for (DeferredLoader dl: deferredLoaders ) {
150 | dl.query(conn);
151 | }
152 |
153 | // I want to make sure we keep no pointers to the object we just created.
154 | deferredLoaders.clear();
155 | return results;
156 | }
157 |
158 | /**
159 | * Runs a query for this object by passing the WHERE ... ORDER BY ... clauses.
160 | *
161 | *
162 | * @param conn
163 | * @param sqlWhere
164 | * @return
165 | * @throws SQLException
166 | */
167 |
168 | public List query(Connection conn, final String sqlWhere) throws SQLException {
169 |
170 | SqlTable table = table();
171 |
172 | if ( table == null ) {
173 | throw new SQLException("Table appears to be undefined. You should use the queryDirect() method.");
174 | }
175 |
176 | StringBuilder sbFields = new StringBuilder();
177 | sbFields.append( table.getPk().name );
178 |
179 | for ( SqlField f: table.values() ) {
180 | sbFields.append(", ").append( f.name );
181 | }
182 |
183 | String joins = "";
184 | String tables = table.name;
185 |
186 | StringBuilder sb = new StringBuilder();
187 |
188 | sb.append( "SELECT ").append( sbFields ).append( "\n" )
189 | .append( " FROM ").append( tables ).append( "\n");
190 |
191 | if (joins.length() > 0 ) {
192 | sb.append( joins ).append(" \n ");
193 | }
194 |
195 | sb.append( sqlWhere );
196 |
197 | return queryDirect(conn, sb.toString() );
198 | }
199 |
200 | /**
201 | * Loads one specific object by primary key.
202 | * If no object is found (or moe than one, but that's unlikely), raises an exception.
203 | *
204 | * @param conn
205 | * @param usedAs
206 | * @return
207 | * @throws SQLException
208 | */
209 | public T get( Connection conn, int pk ) throws SQLException {
210 | List results = getAll( conn, Arrays.asList( new String[]{ "" + pk } ));
211 | return results.get(0);
212 | }
213 |
214 |
215 | /**
216 | * Get all items by PK.
217 | * Mke sure that we get the _same_ number of results we have in our set of
218 | * unique inputs.
219 | *
220 | * @param conn
221 | * @param pks
222 | * @return
223 | * @throws SQLException
224 | */
225 |
226 |
227 | public List getAll( Connection conn, List pks ) throws SQLException {
228 |
229 | if ( pks.isEmpty() ) {
230 | return Collections.EMPTY_LIST;
231 | }
232 |
233 | Set sKeys = new HashSet( pks );
234 |
235 | SqlField myPkField = table().getPk();
236 |
237 | StringBuilder sb = new StringBuilder();
238 | sb.append( " WHERE ").append( myPkField.name ).append( " IN ( ");
239 | SqlTools.addListToStringBuilder(sb, sKeys, ", ", "-1") ;
240 | sb.append( " ) ");
241 |
242 | List results = query( conn, sb.toString() );
243 |
244 | if ( results.size() != sKeys.size() ) {
245 | throw new SQLException("Expected " + sKeys.size() + " results but only got " + results.size() + " when searching by PK: " + sb.toString() );
246 | }
247 |
248 | return results;
249 |
250 | }
251 |
252 | /**
253 | *
254 | * @param conn
255 | * @param object
256 | * @param parentPk
257 | * @throws SQLException
258 | */
259 |
260 | public void commit( Connection conn, T object, String parentPk ) throws SQLException {
261 |
262 | FieldSet fs = new FieldSet(table());
263 | save(object, fs);
264 |
265 | if ( !parentPk.isEmpty() ) {
266 | SqlField parentPkF = table().getParentPk();
267 | if ( parentPkF == null ) {
268 | throw new SQLException( "The table does not have a parentPk field");
269 | } else {
270 | fs.set(parentPkF.name, new StringValue(parentPk) );
271 | }
272 | }
273 |
274 | SqlUpdater su = new SqlUpdater(fs);
275 |
276 | if (su.isInsert()) {
277 | JdbcPattern pIns = JdbcPattern.insert(conn, su.getInsertQuery());
278 | updatePrimaryKey( object, pIns.insertKey );
279 |
280 | } else {
281 | JdbcPattern.update(conn, su.getUpdateQuery());
282 | }
283 |
284 | // if we have any sub objects, let's save them.
285 | saveSubObjects(conn, object);
286 |
287 | }
288 |
289 |
290 |
291 | /**
292 | * Saves an object to the database.
293 | *
294 | * @param conn
295 | * @param object
296 | * @throws SQLException
297 | */
298 |
299 | public void commit(Connection conn, T object) throws SQLException {
300 | commit( conn, object, "");
301 | }
302 |
303 | /**
304 | * Commits a set of objects having the same parentPk.
305 | *
306 | * @param conn
307 | * @param objects
308 | * @param parentPk
309 | * @throws SQLException
310 | */
311 |
312 | public void commitAll( Connection conn, Collection objects, String parentPk ) throws SQLException {
313 | for (T o: objects) {
314 | commit( conn, o, parentPk);
315 | }
316 | }
317 |
318 | /**
319 | * Commit a set of objects.
320 | *
321 | * @param conn
322 | * @param objects
323 | * @throws SQLException
324 | */
325 |
326 | public void commitAll( Connection conn, Collection objects ) throws SQLException {
327 | commitAll( conn, objects, "");
328 | }
329 |
330 |
331 | }
332 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/SqlTools.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync;
2 |
3 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
4 | import java.sql.Connection;
5 | import java.sql.DriverManager;
6 | import java.sql.ResultSet;
7 | import java.sql.SQLException;
8 | import java.text.SimpleDateFormat;
9 | import java.util.ArrayList;
10 | import java.util.Collection;
11 | import java.util.Date;
12 | import java.util.List;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | /**
17 | *
18 | *
19 | * @author lenz
20 | */
21 | public class SqlTools {
22 |
23 | public static final Logger logger = LoggerFactory.getLogger(SqlTools.class);
24 |
25 | /**
26 | * Opens a connection.
27 | *
28 | * @param connString
29 | * @return a working connection.
30 | * @throws SQLException
31 | */
32 | public static Connection openConnection(String connString) throws SQLException {
33 | Connection conn = DriverManager.getConnection( connString );
34 | return conn;
35 | }
36 |
37 | /**
38 | * Closes the connection. No matter what.
39 | *
40 | * @param conn
41 | */
42 | public static void close(Connection conn) {
43 | if (conn != null) {
44 | try {
45 | conn.close();
46 | } catch (SQLException e) {
47 | logger.error("Error closing connection", e);
48 | }
49 | }
50 | }
51 |
52 | public static void execSql( Connection conn, String sql ) throws SQLException {
53 |
54 | ObjectiveFetch of = new ObjectiveFetch() {
55 |
56 | @Override
57 | public SqlTable table() {
58 | return null;
59 | }
60 |
61 | @Override
62 | public Object load(ResultSet rs) throws SQLException {
63 | return null;
64 | }
65 | };
66 |
67 | of.query(conn, sql);
68 | }
69 |
70 |
71 | /**
72 | * Convert any string to a number. If invalid, returns zero.
73 | *
74 | * @param s
75 | * @return the number; if empty or invalid, zero.
76 | */
77 | public static int cint(String s) {
78 | try {
79 | return Integer.parseInt(s);
80 | } catch (NumberFormatException e) {
81 | return 0;
82 | }
83 | }
84 |
85 | public static String asDate(Date d) {
86 | return gtData(SQL_DATE, d);
87 |
88 | }
89 |
90 | public static String asDateTime(Date d) {
91 | return gtData(SQL_DATETIME, d);
92 | }
93 |
94 | /**
95 | * Formatta una data.
96 | * Essitono alcune costanti nell'oggetto U definite per i casi pi? comuni
97 | * Se la data passata ? "null", ritorna la stringa vuota.
98 | *
99 | * @param stFormato Vedi le costanti
100 | * @param dtData Oggetto Date valido
101 | * @return Stringa formattata
102 | */
103 | public static String gtData(String stFormato, Date dtData) {
104 |
105 | if (dtData == null) {
106 | return "";
107 | }
108 |
109 | SimpleDateFormat formatter = new SimpleDateFormat(stFormato);
110 | return formatter.format(dtData);
111 | }
112 | public static final String SQL_DATE = "yyyy-MM-dd";
113 | public static final String SQL_DATETIME = "yyyy-MM-dd HH:mm:ss";
114 |
115 | public static String qq(String s) {
116 | StringBuilder sb = new StringBuilder(s.length() + 5);
117 | return pvtQuoteString(sb, s, " '", "' ");
118 | }
119 |
120 | public static String quoteFieldName(String s) {
121 | StringBuilder sb = new StringBuilder(s.length() + 5);
122 | return pvtQuoteString(sb, s, " `", "` ");
123 | }
124 |
125 | /**
126 | * Implementation of SQL quoting with an optional start and end (usually
127 | * the " sign plus relevant spaces).
128 | *
129 | *
130 | * @param sb builder
131 | * @param s value
132 | * @param prefix the prefix
133 | * @param postfix the postfix
134 | * @return a quoted string
135 | */
136 |
137 | private static String pvtQuoteString(StringBuilder sb, String s, String prefix, String postfix) {
138 |
139 | if (s == null) {
140 | s = "";
141 | }
142 |
143 | sb.append(prefix);
144 |
145 | char c;
146 | for (int i = 0; i < s.length(); i++) {
147 | c = s.charAt(i);
148 |
149 | // trasforma
150 | // ' -> \'
151 | // \n -> {nullo}
152 | // \r -> {nullo}
153 | // \ -> \\
154 | if (c == '\'') {
155 | sb.append("\\'");
156 | } else if (c == '\n') {
157 | sb.append("\\n");
158 | } else if (c == '\r') {
159 | sb.append("\\r");
160 | } else if (c == '\\') {
161 | sb.append("\\\\");
162 | } else {
163 | sb.append(c);
164 | }
165 | }
166 |
167 | sb.append(postfix);
168 | return sb.toString();
169 | }
170 |
171 | public static void addListToStringBuilder(StringBuilder sb, Collection lS, String separator, String emptyCase) {
172 |
173 | if (lS.isEmpty()) {
174 | sb.append(emptyCase);
175 |
176 | } else {
177 |
178 | int pos = 0;
179 | for (String val : lS) {
180 | if (pos > 0) {
181 | sb.append(separator);
182 | }
183 | sb.append(val);
184 | pos++;
185 | }
186 | }
187 |
188 | }
189 |
190 | /**
191 | * Gets us an IN( ... ) clause backed by enums.
192 | *
193 | * @param clauses
194 | * @param emptyList
195 | * @return
196 | */
197 |
198 | public static String quoteInEnum( List extends Enum> clauses, String emptyList ) {
199 |
200 | List vals = new ArrayList();
201 | for ( Enum e: clauses ) {
202 | vals.add(e.name());
203 | }
204 |
205 | return quoteInClause(vals, emptyList);
206 |
207 | }
208 |
209 | /**
210 | * An IN( ... ) clause backed by a List of Strings.
211 | *
212 | * @param clauses
213 | * @param emptyList
214 | * @return
215 | */
216 |
217 | public static String quoteInClause( List clauses, String emptyList ) {
218 | StringBuilder sb = new StringBuilder();
219 | SqlTools.addListToStringBuilder(sb, quoteList(clauses, emptyList), ", ", "");
220 | return sb.toString();
221 | }
222 |
223 | /**
224 | * Quotes a list of items and separates them (usually with a comma or similar).
225 | *
226 | * @param values
227 | * @param emptyCase
228 | * @return
229 | */
230 |
231 | public static List quoteList( List values, String emptyCase ) {
232 | List out = new ArrayList( values.size() );
233 |
234 | if ( values.isEmpty() ) {
235 | out.add( SqlTools.qq(emptyCase) );
236 | } else {
237 | for ( String s: values ) {
238 | out.add( SqlTools.qq(s) );
239 | }
240 | }
241 | return out;
242 | }
243 |
244 |
245 | }
246 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/SqlField.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.table;
2 |
3 | import ch.loway.oss.ObjectiveSync.table.type.FuncValue;
4 | import ch.loway.oss.ObjectiveSync.table.type.TableValue;
5 |
6 | /**
7 | * This immutable object describes a table field.
8 | *
9 | * @author lenz
10 | */
11 | public class SqlField {
12 |
13 | public final String name;
14 | public final String type;
15 | public final UsedAs usedAs;
16 | public final TableValue defaultInsert;
17 | public final TableValue defaultUpdate;
18 |
19 | /**
20 | * Creator.
21 | *
22 | * @param name
23 | * @param type
24 | * @param usedAs
25 | * @param defaultInsert
26 | * @param defaultUpdate
27 | */
28 |
29 | public SqlField(String name, String type, UsedAs usedAs, TableValue defaultInsert, TableValue defaultUpdate) {
30 | this.name = name;
31 | this.type = type;
32 | this.usedAs = usedAs;
33 | this.defaultInsert = defaultInsert;
34 | this.defaultUpdate = defaultUpdate;
35 | }
36 |
37 | /**
38 | * Creates a PK field.
39 | *
40 | * @param name
41 | * @param def
42 | * @param funcDefInsert
43 | * @return a PK field.
44 | */
45 | public static SqlField pk(String name, String def, String funcDefInsert) {
46 | return new SqlField(name, def, UsedAs.PK, new FuncValue(funcDefInsert), null);
47 | }
48 |
49 | public static SqlField parentPk(String name, String def, String funcDefInsert) {
50 | return new SqlField(name, def, UsedAs.PARENT_PK, new FuncValue(funcDefInsert), null);
51 | }
52 |
53 | public static SqlField str(String name, String def, TableValue defInsert, TableValue defUpdate) {
54 | return new SqlField(name, def, UsedAs.PLAIN, defInsert, defUpdate);
55 | }
56 |
57 | public static SqlField i(String name, String def, TableValue defInsert, TableValue defUpdate) {
58 | return new SqlField(name, def, UsedAs.PLAIN, defInsert, defUpdate);
59 | }
60 |
61 |
62 |
63 | /**
64 | * Does this field have any special reason to be here?
65 | */
66 |
67 | public static enum UsedAs {
68 | PLAIN,
69 | PK,
70 | PARENT_PK
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/SqlTable.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.table;
3 |
4 | import java.util.ArrayList;
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | /**
9 | * Describes a table as a set of fields (columns).
10 | *
11 | * @author lenz
12 | */
13 | public class SqlTable {
14 |
15 | public final String name;
16 | List fields = new ArrayList();
17 |
18 | public SqlTable( String tableName ) {
19 | name = tableName;
20 | }
21 |
22 | public SqlTable field( SqlField f ) {
23 | fields.add( f );
24 | return this;
25 | }
26 |
27 | /**
28 | * Gets us the PK field.
29 | *
30 | * @return the PK in use
31 | */
32 |
33 | public SqlField getPk() {
34 | return getFieldUsedFor(SqlField.UsedAs.PK);
35 |
36 | }
37 |
38 | /**
39 | * Get the Parents' PK field
40 | * @return
41 | */
42 |
43 | public SqlField getParentPk() {
44 | return getFieldUsedFor(SqlField.UsedAs.PARENT_PK);
45 | }
46 |
47 | /**
48 | * Finds a field.
49 | *
50 | * @param usage
51 | * @return
52 | */
53 |
54 | private SqlField getFieldUsedFor( SqlField.UsedAs usage ) {
55 | for ( SqlField f: fields ) {
56 | if ( f.usedAs == usage ) {
57 | return f;
58 | }
59 | }
60 | return null;
61 | }
62 |
63 |
64 | /**
65 | * Is a field defined for this table?
66 | *
67 | * @param name
68 | * @return true if the field is currently defined.
69 | */
70 |
71 | public boolean fieldExists( String name ) {
72 | for ( SqlField f: fields ) {
73 | if ( f.name.equals(name) ) {
74 | return true;
75 | }
76 | }
77 | return false;
78 | }
79 |
80 | /**
81 | * A collection of columns describing the table.
82 | *
83 | * @return the columns
84 | */
85 |
86 | public List values() {
87 | return Collections.unmodifiableList(fields);
88 | }
89 |
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/type/FuncValue.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.table.type;
3 |
4 | /**
5 | *
6 | *
7 | * $Id$
8 | * @author lenz
9 | */
10 | public class FuncValue extends TableValue {
11 |
12 | private final String func;
13 |
14 | @Override
15 | public String embeddableValue() {
16 | return func;
17 | }
18 |
19 | public FuncValue(String func) {
20 | this.func = func;
21 | }
22 | }
23 |
24 | // $Log$
25 | //
26 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/type/IntValue.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.table.type;
3 |
4 | /**
5 | *
6 | *
7 | * $Id$
8 | * @author lenz
9 | */
10 | public class IntValue extends TableValue {
11 | private final int val;
12 |
13 | @Override
14 | public String embeddableValue() {
15 | return Integer.toString(val);
16 | }
17 |
18 | public IntValue(int val) {
19 | this.val = val;
20 | }
21 | }
22 |
23 | // $Log$
24 | //
25 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/type/StringValue.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.table.type;
3 |
4 | import ch.loway.oss.ObjectiveSync.SqlTools;
5 |
6 | /**
7 | *
8 | *
9 | * $Id$
10 | * @author lenz
11 | */
12 | public class StringValue extends TableValue {
13 |
14 | private final String val;
15 |
16 | @Override
17 | public String embeddableValue() {
18 | return SqlTools.qq(val);
19 | }
20 |
21 | public StringValue(String val) {
22 | this.val = val;
23 | }
24 |
25 |
26 |
27 |
28 |
29 | }
30 |
31 | // $Log$
32 | //
33 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/table/type/TableValue.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.table.type;
3 |
4 | /**
5 | *
6 | *
7 | * $Id$
8 | * @author lenz
9 | */
10 | public abstract class TableValue {
11 |
12 | public abstract String embeddableValue();
13 |
14 | }
15 |
16 | // $Log$
17 | //
18 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/updater/FieldSet.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.updater;
2 |
3 | import ch.loway.oss.ObjectiveSync.table.SqlField;
4 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
5 | import ch.loway.oss.ObjectiveSync.table.type.TableValue;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * A set of fields, ready to be written to the DB:
11 | *
12 | * @author lenz
13 | */
14 | public class FieldSet {
15 |
16 | SqlTable table;
17 | Map defFields = new HashMap();
18 |
19 | public FieldSet(SqlTable referenceTable) {
20 | table = referenceTable;
21 | }
22 |
23 | public FieldSet set(String name, TableValue t) {
24 | if (table.fieldExists(name)) {
25 | defFields.put(name, t);
26 | } else {
27 | throw new IllegalArgumentException("Undefined field: '" + name + "' in table ");
28 | }
29 | return this;
30 | }
31 |
32 | /**
33 | * Did we set a PK for this transaction?
34 | *
35 | * @return true if a PK is defined; false if none (insert).
36 | */
37 | public boolean isPkSet() {
38 | SqlField pk = table.getPk();
39 | return (defFields.containsKey(pk.name));
40 | }
41 |
42 | /**
43 | * What is the PK field to be used?
44 | *
45 | * @return the PK field
46 | */
47 | public Map getPkValue() {
48 | Map hmOut = new HashMap();
49 |
50 | SqlField pk = table.getPk();
51 | TableValue tv = defFields.get(pk.name);
52 | String quotedVal = "";
53 |
54 | if (tv == null) {
55 | quotedVal = pk.defaultInsert.embeddableValue();
56 | } else {
57 | quotedVal = tv.embeddableValue();
58 | }
59 |
60 | hmOut.put(pk.name, quotedVal);
61 | return hmOut;
62 | }
63 |
64 | /**
65 | * Gets all values but the PK.
66 | *
67 | * @return a map of all fields of this table.
68 | */
69 | public Map getValuesButPk() {
70 |
71 | Map hmOut = new HashMap();
72 | for (SqlField f : table.values()) {
73 | if ( f.usedAs != SqlField.UsedAs.PK) {
74 |
75 | if (defFields.containsKey(f.name)) {
76 |
77 | TableValue val = defFields.get(f.name);
78 | hmOut.put(f.name, val.embeddableValue());
79 |
80 | } else {
81 |
82 | TableValue tv = null;
83 | if (isPkSet()) {
84 | tv = f.defaultUpdate;
85 | } else {
86 | tv = f.defaultInsert;
87 | }
88 |
89 | // se non ho un field e questo non ha default, proprio lo ignoro
90 | if (tv != null) {
91 | hmOut.put(f.name, tv.embeddableValue());
92 | }
93 | }
94 | }
95 | }
96 |
97 | return hmOut;
98 |
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/updater/SqlFieldVal.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.updater;
2 |
3 | public class SqlFieldVal {
4 |
5 | // SqlField field;
6 | // TableValue currentValue;
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/updater/SqlUpdater.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.updater;
3 |
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 |
9 | /**
10 | * Builds insert and update queries.
11 | *
12 | * @author lenz
13 | */
14 | public class SqlUpdater {
15 |
16 | private final FieldSet fs;
17 |
18 | public SqlUpdater(FieldSet fs) {
19 | this.fs = fs;
20 | }
21 |
22 | /**
23 | * Builds an INSERT query for the object.
24 | * The PK is assumed to be auto-generated.
25 | *
26 | * @return SQL query as a string
27 | */
28 |
29 | public String getInsertQuery() {
30 |
31 | if ( fs.isPkSet() ) {
32 | throw new IllegalStateException("This is not an INSERT query");
33 | }
34 |
35 | String tableName = fs.table.name;
36 | Map mVals = fs.getValuesButPk();
37 |
38 | List fields = new ArrayList(mVals.keySet());
39 | List vals = new ArrayList();
40 | for ( String fieldName: fields ) {
41 | vals.add( mVals.get(fieldName) );
42 | }
43 |
44 | StringBuilder sb = new StringBuilder();
45 | sb.append( "INSERT INTO " ).append( tableName ).append( " ( ");
46 |
47 | for ( int i=0; i < fields.size(); i++ ) {
48 | sb.append( (i>0) ? ", " : "" );
49 | sb.append( fields.get(i) );
50 | }
51 |
52 | sb.append( " ) VALUES ( ");
53 |
54 | for ( int i=0; i < vals.size(); i++ ) {
55 | sb.append( (i>0) ? ", " : "" );
56 | sb.append( vals.get(i) );
57 | }
58 |
59 | sb.append( ") ");
60 | return sb.toString();
61 | }
62 |
63 | /**
64 | * Builds an update query.
65 | *
66 | * We expect this to be by PK.
67 | * \todo handling Optilock
68 | *
69 | * @return SQL query as a string
70 | */
71 |
72 | public String getUpdateQuery() {
73 |
74 | if ( !fs.isPkSet() ) {
75 | throw new IllegalStateException("This is not an UPDATE query");
76 | }
77 |
78 | String tableName = fs.table.name;
79 | Map mVals = fs.getValuesButPk();
80 | Map mPk = fs.getPkValue();
81 |
82 | StringBuilder sb = new StringBuilder();
83 | sb.append("UPDATE ").append(tableName).append(" SET ");
84 |
85 | int i = 0;
86 | for ( Entry e: mVals.entrySet()) {
87 |
88 | sb.append( (i>0) ? ", " : "" );
89 | sb.append( e.getKey() ).append( "=" ).append( e.getValue() );
90 | i++;
91 | }
92 |
93 | sb.append(" WHERE ");
94 | for ( Entry e: mPk.entrySet()) {
95 |
96 | sb.append( e.getKey() ).append( " = " ).append( e.getValue() );
97 |
98 | }
99 |
100 | return sb.toString();
101 | }
102 |
103 | /**
104 | * Checks if we have a defined PK (so we UPDATE) the object
105 | * or we do not have it and so we INSERT.
106 | *
107 | * @return whether the PK is defined
108 | */
109 |
110 | public boolean isInsert() {
111 | return !fs.isPkSet();
112 | }
113 |
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/updater/deferred/DeferredLoadByParent.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.updater.deferred;
3 |
4 | import ch.loway.oss.ObjectiveSync.updater.deferred.DeferredLoader;
5 | import ch.loway.oss.ObjectiveSync.ObjectiveFetch;
6 |
7 | /**
8 | *
9 | *
10 | * @author lenz
11 | */
12 | public abstract class DeferredLoadByParent extends DeferredLoader {
13 |
14 | String whereClause = "";
15 |
16 | public DeferredLoadByParent setup( ObjectiveFetch fetcher, int pkId ) {
17 | this.of = fetcher;
18 |
19 | String field = fetcher.table().getParentPk().name;
20 | whereClause = "WHERE " + field + " = " + pkId;
21 | return this;
22 | }
23 |
24 | @Override
25 | public String getWhereClause() {
26 | return whereClause;
27 | }
28 |
29 |
30 | }
31 |
32 | // $Log$
33 | //
34 |
--------------------------------------------------------------------------------
/classes/ch/loway/oss/ObjectiveSync/updater/deferred/DeferredLoader.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.updater.deferred;
3 |
4 | import ch.loway.oss.ObjectiveSync.ObjectiveFetch;
5 | import java.sql.Connection;
6 | import java.sql.SQLException;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | /**
12 | * Does deferred loading.
13 | *
14 | * @author lenz
15 | */
16 | public abstract class DeferredLoader {
17 |
18 | public ObjectiveFetch of = null;
19 |
20 | public abstract void update( Set sons );
21 |
22 |
23 | public void query( Connection conn ) throws SQLException {
24 | List results = of.query(conn, getWhereClause() );
25 | Set sResults = new HashSet( results );
26 | update( sResults );
27 | }
28 |
29 | public abstract String getWhereClause();
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/CSTest.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | import org.junit.After;
5 | import org.junit.AfterClass;
6 | import org.junit.Before;
7 | import org.junit.BeforeClass;
8 | import org.junit.Test;
9 | import static org.junit.Assert.*;
10 |
11 | /**
12 | *
13 | * @author lenz
14 | */
15 | public class CSTest {
16 |
17 | public CSTest() {
18 | }
19 |
20 | @BeforeClass
21 | public static void setUpClass() throws Exception {
22 | }
23 |
24 | @AfterClass
25 | public static void tearDownClass() throws Exception {
26 | }
27 |
28 | @Before
29 | public void setUp() {
30 | }
31 |
32 | @After
33 | public void tearDown() {
34 | }
35 |
36 | @Test
37 | public void testSomeMethod() {
38 | assertTrue("Have version", CS.VERSION.length() > 0 );
39 | }
40 |
41 | }
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/JdbcPatternTest.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | import java.sql.Connection;
5 | import java.sql.SQLException;
6 | import java.sql.Statement;
7 | import org.junit.After;
8 | import org.junit.AfterClass;
9 | import org.junit.Before;
10 | import org.junit.BeforeClass;
11 | import org.junit.Test;
12 | import static org.junit.Assert.*;
13 |
14 | /**
15 | * Now testing the JdbcPattern object
16 | *
17 | * @author lenz
18 | */
19 | public class JdbcPatternTest {
20 |
21 | public JdbcPatternTest() {
22 | }
23 |
24 | Connection conn = null;
25 |
26 | @BeforeClass
27 | public static void setUpClass() throws ClassNotFoundException {
28 | Class.forName("org.h2.Driver");
29 | }
30 |
31 | @AfterClass
32 | public static void tearDownClass() {
33 | }
34 |
35 | @Before
36 | public void setUp() throws SQLException {
37 | conn = SqlTools.openConnection("jdbc:h2:mem:test");
38 | String sql = "CREATE TABLE EXAMPLE ( id int auto_increment, name char(50), surname char(50) )";
39 |
40 | Statement stmt = conn.createStatement();
41 | stmt.execute(sql);
42 |
43 | }
44 |
45 | @After
46 | public void tearDown() throws SQLException {
47 | conn.close();
48 | }
49 |
50 | /**
51 | * Test inserting into the DB.
52 | */
53 | @Test
54 | public void testInsert() throws Exception {
55 |
56 | JdbcPattern pInsert = JdbcPattern.insert(conn, "INSERT INTO EXAMPLE (name, surname) VALUES ('A', 'B')" );
57 |
58 | System.out.println( "Gen key:" + pInsert.insertKey );
59 | assertTrue( "Chiave generata", pInsert.insertKey.length() > 0);
60 | }
61 |
62 | @Test
63 | public void testUpdate() throws Exception {
64 | JdbcPattern pInsert = JdbcPattern.insert(conn, "INSERT INTO EXAMPLE (name, surname) VALUES ('A', 'B')" );
65 |
66 | JdbcPattern.update(conn, "UPDATE EXAMPLE SET surname ='X' WHERE id=" + pInsert.insertKey );
67 | }
68 |
69 | @Test
70 | public void testExec() throws Exception {
71 | JdbcPattern pExec = JdbcPattern.exec(conn,
72 | "CREATE TABLE EXAMPLE2 ( id int auto_increment, name char(50), surname char(50) )" );
73 |
74 |
75 | }
76 |
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/ObjectiveFetchTest.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | import java.util.ArrayList;
5 | import java.util.Set;
6 | import java.util.List;
7 | import ch.loway.oss.ObjectiveSync.maps.Person;
8 | import ch.loway.oss.ObjectiveSync.maps.PersonDB;
9 | import java.sql.Connection;
10 | import java.sql.SQLException;
11 | import java.util.HashSet;
12 | import org.junit.After;
13 | import org.junit.AfterClass;
14 | import org.junit.Before;
15 | import org.junit.BeforeClass;
16 | import org.junit.Test;
17 | import static org.junit.Assert.*;
18 |
19 | /**
20 | *
21 | * @author lenz
22 | */
23 | public class ObjectiveFetchTest {
24 |
25 | Connection conn = null;
26 |
27 | public ObjectiveFetchTest() {
28 | }
29 |
30 | @BeforeClass
31 | public static void setUpClass() throws Exception {
32 | }
33 |
34 | @AfterClass
35 | public static void tearDownClass() throws Exception {
36 | }
37 |
38 | @Before
39 | public void setUp() throws ClassNotFoundException, SQLException {
40 | Class.forName("org.h2.Driver");
41 | conn = SqlTools.openConnection("jdbc:h2:mem:test");
42 |
43 | String sql = "CREATE TABLE person ( id int auto_increment, name char(50), surname char(50), org_id int )";
44 | JdbcPattern.exec(conn, sql);
45 |
46 | }
47 |
48 | @After
49 | public void tearDown() {
50 | SqlTools.close(conn);
51 | conn = null;
52 | }
53 |
54 | @Test
55 | public void testPlain() throws SQLException {
56 |
57 | PersonDB of = new PersonDB();
58 |
59 | Person p = Person.build(0, "ike", "boo");
60 | of.commit(conn, p);
61 |
62 | List lP = of.queryDirect(conn, "SELECT * FROM person");
63 | assertEquals( "N persons", 1, lP.size());
64 |
65 | }
66 |
67 | @Test
68 | public void testSetPKonInsert() throws SQLException {
69 |
70 | PersonDB of = new PersonDB();
71 |
72 | Person p = Person.build(0, "ike", "boo");
73 | of.commit(conn, p);
74 |
75 | assertTrue( "PK should be set", (p.id != 0) );
76 |
77 |
78 | }
79 |
80 | @Test
81 | public void testCommitAll() throws SQLException {
82 |
83 | PersonDB of = new PersonDB();
84 |
85 | Set collection = new HashSet();
86 |
87 | collection.add( Person.build(0, "A", "B") );
88 | collection.add( Person.build(0, "C", "D") );
89 | collection.add( Person.build(0, "E", "F") );
90 |
91 | of.commitAll(conn, collection);
92 |
93 | List lP = of.queryDirect(conn, "SELECT * FROM person");
94 | assertEquals( "N persons", 3, lP.size());
95 | }
96 |
97 |
98 | @Test
99 | public void testQuery_plain() throws SQLException {
100 |
101 | PersonDB of = new PersonDB();
102 |
103 | Person p1 = Person.build(0, "jabba", "the hutt");
104 | Person p2 = Person.build(0, "luke", "skywalker");
105 | of.commit(conn, p1);
106 | of.commit(conn, p2);
107 |
108 | List lP = of.query(conn, "WHERE name = 'luke'");
109 | assertEquals( "N persons", 1, lP.size());
110 | assertEquals("Surname", "skywalker", lP.get(0).surname );
111 | assertTrue( "ID set", lP.get(0).id != 0 );
112 |
113 | }
114 |
115 |
116 | @Test
117 | public void testGetAll() throws SQLException {
118 |
119 | PersonDB of = new PersonDB();
120 |
121 | List collection = new ArrayList();
122 | collection.add( Person.build(0, "A", "B") );
123 | collection.add( Person.build(0, "C", "D") );
124 | collection.add( Person.build(0, "E", "F") );
125 | of.commitAll(conn, collection);
126 |
127 | // Load all PKs
128 | List lQ = new ArrayList();
129 | for ( Person p: collection) {
130 | lQ.add( "" + p.id );
131 | }
132 |
133 | List lP = of.getAll( conn, lQ );
134 | assertEquals( "N persons", 3, lP.size());
135 | }
136 |
137 | @Test
138 | public void testGetAll_wrongKeyAdded() throws SQLException {
139 |
140 | PersonDB of = new PersonDB();
141 |
142 | List collection = new ArrayList();
143 | collection.add( Person.build(0, "A", "B") );
144 | collection.add( Person.build(0, "C", "D") );
145 | collection.add( Person.build(0, "E", "F") );
146 | of.commitAll(conn, collection);
147 |
148 | // Load all PKs
149 | List lQ = new ArrayList();
150 | for ( Person p: collection) {
151 | lQ.add( "" + p.id );
152 | }
153 | lQ.add( "19137" );
154 |
155 | try {
156 | List lP = of.getAll( conn, lQ );
157 | } catch ( SQLException e) {
158 | System.out.println( e.toString() );
159 | return;
160 | }
161 | fail( "No exception raised!");
162 |
163 |
164 | }
165 |
166 |
167 |
168 | }
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/ObjectiveFetch_1N_Test.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | import ch.loway.oss.ObjectiveSync.JdbcPattern;
5 | import ch.loway.oss.ObjectiveSync.SqlTools;
6 | import java.util.ArrayList;
7 | import ch.loway.oss.ObjectiveSync.maps.Organization;
8 | import ch.loway.oss.ObjectiveSync.maps.OrganizationDB;
9 | import java.util.List;
10 | import ch.loway.oss.ObjectiveSync.maps.Person;
11 | import java.sql.Connection;
12 | import java.sql.SQLException;
13 | import org.junit.After;
14 | import org.junit.AfterClass;
15 | import org.junit.Before;
16 | import org.junit.BeforeClass;
17 | import org.junit.Test;
18 | import static org.junit.Assert.*;
19 |
20 | /**
21 | * tests ObjectiveFetch in 1:N associations.
22 | *
23 | * @author lenz
24 | */
25 | public class ObjectiveFetch_1N_Test {
26 |
27 | Connection conn = null;
28 |
29 | public ObjectiveFetch_1N_Test() {
30 | }
31 |
32 | @BeforeClass
33 | public static void setUpClass() throws Exception {
34 | }
35 |
36 | @AfterClass
37 | public static void tearDownClass() throws Exception {
38 | }
39 |
40 | @Before
41 | public void setUp() throws ClassNotFoundException, SQLException {
42 | Class.forName("org.h2.Driver");
43 | conn = SqlTools.openConnection("jdbc:h2:mem:test");
44 |
45 | JdbcPattern.execAll(conn, new String[] {
46 | "CREATE TABLE person ( id int auto_increment, name char(50), surname char(50), org_id int )",
47 | "CREATE TABLE org ( id int auto_increment, name char(50) )"
48 | });
49 |
50 |
51 | }
52 |
53 | @After
54 | public void tearDown() {
55 | SqlTools.close(conn);
56 | conn = null;
57 | }
58 |
59 | @Test
60 | public void testPlain() throws SQLException {
61 |
62 | OrganizationDB db = new OrganizationDB();
63 |
64 | Organization org = Organization.build(0, "Evil empire");
65 | org.addMember( Person.build(0, "Emperor", "Evil"));
66 | org.addMember( Person.build( 0, "Darth", "Vader"));
67 |
68 | db.commit(conn, org);
69 |
70 | List lP = db.query(conn, "");
71 | assertEquals( "N orgs", 1, lP.size());
72 | assertEquals( "People linked", 2, lP.get(0).members.size() );
73 | }
74 |
75 |
76 | @Test
77 | public void testWrite() throws SQLException {
78 |
79 | OrganizationDB db = new OrganizationDB();
80 |
81 | Organization org = Organization.build(0, "Evil empire");
82 | org.addMember( Person.build(0, "Emperor", "Evil"));
83 | org.addMember( Person.build( 0, "Darth", "Vader"));
84 |
85 | db.commit(conn, org);
86 |
87 | // check that objects do have a PK (so we know they were saved).
88 | List members = new ArrayList( org.members );
89 | assertTrue( "Org has PK", org.id != 0 );
90 | assertTrue( "Org.m0 has PK", members.get(0).id != 0 );
91 | assertTrue( "Org.m1 has PK", members.get(1).id != 0 );
92 |
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/ObjectiveFetch_ReadOnlyTest.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync;
3 |
4 | import ch.loway.oss.ObjectiveSync.maps.ACInfo;
5 | import ch.loway.oss.ObjectiveSync.maps.ACInfoDB;
6 | import java.util.List;
7 | import java.sql.Connection;
8 | import java.sql.SQLException;
9 | import org.junit.After;
10 | import org.junit.AfterClass;
11 | import org.junit.Before;
12 | import org.junit.BeforeClass;
13 | import org.junit.Test;
14 | import static org.junit.Assert.*;
15 |
16 | /**
17 | * Tests ObjectiveFetch in 1:N associations.
18 | *
19 | * @author lenz
20 | */
21 | public class ObjectiveFetch_ReadOnlyTest {
22 |
23 | Connection conn = null;
24 |
25 | public ObjectiveFetch_ReadOnlyTest() {
26 | }
27 |
28 | @BeforeClass
29 | public static void setUpClass() throws Exception {
30 | }
31 |
32 | @AfterClass
33 | public static void tearDownClass() throws Exception {
34 | }
35 |
36 | @Before
37 | public void setUp() throws ClassNotFoundException, SQLException {
38 | Class.forName("org.h2.Driver");
39 | conn = SqlTools.openConnection("jdbc:h2:mem:test");
40 |
41 | JdbcPattern.execAll(conn, new String[] {
42 | "CREATE TABLE campaigns ( campaignId int, name char(50), pace char(50), securityKey char(50) )",
43 | "CREATE TABLE hopper ( campaign int, runMode char(50) )"
44 | });
45 |
46 |
47 | }
48 |
49 | @After
50 | public void tearDown() {
51 | SqlTools.close(conn);
52 | conn = null;
53 | }
54 |
55 | @Test
56 | public void testPlainNotRunning() throws SQLException {
57 |
58 | JdbcPattern.execAll(conn, new String[] {
59 | "INSERT INTO campaigns ( campaignId, name, pace, securityKey) VALUES ( 10, 'Notused', 'RUNNABLE', 'xxx' ) "
60 | });
61 |
62 | List lP = ACInfoDB.findAll(conn);
63 | assertEquals( "N info", 1, lP.size());
64 | assertEquals( "#1: Name", "Notused", lP.get(0).name );
65 | assertEquals( "#1: ID", 10, lP.get(0).campaignId );
66 | assertEquals( "#1: SecKey", "xxx", lP.get(0).secKey );
67 | assertEquals( "#1: Running", false, lP.get(0).isRunning );
68 | }
69 |
70 |
71 | @Test
72 | public void testPlainExistsNotRunning() throws SQLException {
73 |
74 | JdbcPattern.execAll(conn, new String[] {
75 | "INSERT INTO campaigns ( campaignId, name, pace, securityKey) VALUES ( 10, 'Notused', 'RUNNABLE', 'xxx' ) ",
76 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'KO_D' ) "
77 |
78 | });
79 |
80 | List lP = ACInfoDB.findAll(conn);
81 | assertEquals( "N info", 1, lP.size());
82 | assertEquals( "#1: Name", "Notused", lP.get(0).name );
83 | assertEquals( "#1: ID", 10, lP.get(0).campaignId );
84 | assertEquals( "#1: SecKey", "xxx", lP.get(0).secKey );
85 | assertEquals( "#1: Running", false, lP.get(0).isRunning );
86 | }
87 |
88 |
89 | @Test
90 | public void testPlainExistsRunning() throws SQLException {
91 |
92 | JdbcPattern.execAll(conn, new String[] {
93 | "INSERT INTO campaigns ( campaignId, name, pace, securityKey) VALUES ( 10, 'Notused', 'RUNNABLE', 'xxx' ) ",
94 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'OK_A' ) "
95 |
96 | });
97 |
98 | List lP = ACInfoDB.findAll(conn);
99 | assertEquals( "N info", 1, lP.size());
100 | assertEquals( "#1: Name", "Notused", lP.get(0).name );
101 | assertEquals( "#1: ID", 10, lP.get(0).campaignId );
102 | assertEquals( "#1: SecKey", "xxx", lP.get(0).secKey );
103 | assertEquals( "#1: Running", true, lP.get(0).isRunning );
104 |
105 |
106 | }
107 |
108 | @Test
109 | public void testPlainExistsRunningIfAtLeastOneRunning() throws SQLException {
110 |
111 | JdbcPattern.execAll(conn, new String[] {
112 | "INSERT INTO campaigns ( campaignId, name, pace, securityKey) VALUES ( 10, 'Notused', 'RUNNABLE', 'xxx' ) ",
113 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'KO_C' ) ",
114 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'KO_D' ) ",
115 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'KO_C' ) ",
116 | "INSERT INTO hopper ( campaign, runMode) VALUES ( 10, 'OK_A' ) "
117 | });
118 |
119 | List lP = ACInfoDB.findAll(conn);
120 | assertEquals( "N info", 1, lP.size());
121 | assertEquals( "#1: Name", "Notused", lP.get(0).name );
122 | assertEquals( "#1: ID", 10, lP.get(0).campaignId );
123 | assertEquals( "#1: SecKey", "xxx", lP.get(0).secKey );
124 | assertEquals( "#1: Running", true, lP.get(0).isRunning );
125 |
126 | }
127 |
128 |
129 | }
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/learnH2/LearnH2Test.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.learnH2;
2 |
3 | import java.sql.Connection;
4 | import java.sql.DriverManager;
5 | import java.sql.SQLException;
6 | import org.junit.After;
7 | import org.junit.AfterClass;
8 | import org.junit.Before;
9 | import org.junit.BeforeClass;
10 | import org.junit.Test;
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | *
15 | * @author lenz
16 | */
17 | public class LearnH2Test {
18 |
19 | public LearnH2Test() {
20 | }
21 |
22 | @BeforeClass
23 | public static void setUpClass() throws Exception {
24 | }
25 |
26 | @AfterClass
27 | public static void tearDownClass() throws Exception {
28 | }
29 |
30 | @Before
31 | public void setUp() {
32 | }
33 |
34 | @After
35 | public void tearDown() {
36 | }
37 |
38 | @Test
39 | public void testInit() throws ClassNotFoundException, SQLException {
40 |
41 |
42 | Class.forName("org.h2.Driver");
43 | Connection conn = DriverManager.getConnection("jdbc:h2:mem:test");
44 | conn.close();
45 |
46 | assertTrue(true);
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/ACInfo.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.maps;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | /**
8 | * This is an aggregation query.
9 | *
10 | * @author lenz
11 | */
12 | public class ACInfo {
13 |
14 | public int campaignId = 0;
15 | public String name = "";
16 | public boolean isRunning = true;
17 | public String secKey = "";
18 |
19 | /**
20 | * This is a simple Enum.
21 | */
22 | public enum RunMode {
23 |
24 | OK_A, OK_B, KO_C, KO_D;
25 |
26 | public static List asList() {
27 | List lOut = new ArrayList();
28 | for ( RunMode rm: values() ) {
29 | if ( rm.toString().startsWith("OK") ) {
30 | lOut.add(rm);
31 | }
32 | }
33 | return lOut;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/ACInfoDB.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.maps;
3 |
4 | import ch.loway.oss.ObjectiveSync.ObjectiveFetch;
5 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
6 | import java.sql.Connection;
7 | import java.sql.ResultSet;
8 | import java.sql.SQLException;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | /**
14 | *
15 | *
16 | * @author lenz
17 | */
18 | public class ACInfoDB extends ObjectiveFetch {
19 |
20 | Map results = new HashMap();
21 |
22 | /**
23 | * Notice a pattern here.
24 | * If we return "null" the object si not added.
25 | * So we return the object only when it's just created.
26 | * If the object is already present, we recover it from the hash table
27 | * and set its value to true.
28 | *
29 | * @param rs
30 | * @return
31 | * @throws SQLException
32 | */
33 |
34 | @Override
35 | public ACInfo load(ResultSet rs) throws SQLException {
36 |
37 | int id = rs.getInt("campaignId");
38 | ACInfo ai = null;
39 |
40 | if ( !results.containsKey(id) ) {
41 | ai = new ACInfo();
42 | ai.campaignId = rs.getInt("campaignId");
43 | ai.name = rs.getString("name");
44 | ai.secKey = rs.getString("securityKey");
45 | ai.isRunning = false;
46 | results.put(id, ai);
47 | }
48 |
49 | String runMode = rs.getString("runMode");
50 | if ( runMode != null ) {
51 | ACInfo.RunMode rMode = ACInfo.RunMode.valueOf(runMode);
52 | if ( ACInfo.RunMode.asList().contains( rMode ) ) {
53 |
54 | ACInfo ai2 = results.get(id);
55 | ai2.isRunning = true;
56 | }
57 | }
58 |
59 | return ai;
60 |
61 | }
62 |
63 | /**
64 | * Gets back a "real" view.
65 | *
66 | * @param conn
67 | * @return
68 | * @throws SQLException
69 | */
70 |
71 | public static List findAll( Connection conn ) throws SQLException {
72 |
73 | ACInfoDB aci = new ACInfoDB();
74 | List lOut = aci.queryDirect(conn,
75 | " SELECT c.campaignId, c.name, c.securityKey, hopper.runMode, count(*) as NUM "
76 | + " FROM campaigns c "
77 | + " LEFT JOIN hopper ON c.campaignId = hopper.campaign "
78 | + " WHERE c.pace = 'RUNNABLE' "
79 | + " GROUP BY c.campaignId, c.name, c.securityKey, hopper.runMode "
80 | );
81 |
82 | return lOut;
83 | }
84 |
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/Organization.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.maps;
3 |
4 | import java.util.HashSet;
5 | import java.util.Set;
6 |
7 | /**
8 | * This class matches an organization that might include one or more
9 | * persons.
10 | *
11 | * @author lenz
12 | */
13 | public class Organization {
14 |
15 | public int id = 0;
16 | public String name = "";
17 | public Set members = new HashSet();
18 |
19 | /**
20 | * Builds this object.
21 | *
22 | * @param id
23 | * @param name
24 | * @return
25 | */
26 |
27 | public static Organization build( int id, String name ) {
28 | Organization o = new Organization();
29 | o.id = id;
30 | o.name = name;
31 | return o;
32 | }
33 |
34 | /**
35 | * Add a member to this org.
36 | *
37 | * @param p
38 | */
39 | public void addMember( Person p ) {
40 | members.add(p);
41 | }
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/OrganizationDB.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.maps;
2 |
3 | import ch.loway.oss.ObjectiveSync.ObjectiveFetch;
4 | import ch.loway.oss.ObjectiveSync.SqlTools;
5 | import ch.loway.oss.ObjectiveSync.table.SqlField;
6 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
7 | import ch.loway.oss.ObjectiveSync.table.type.IntValue;
8 | import ch.loway.oss.ObjectiveSync.table.type.StringValue;
9 | import ch.loway.oss.ObjectiveSync.updater.deferred.DeferredLoadByParent;
10 | import ch.loway.oss.ObjectiveSync.updater.FieldSet;
11 | import java.sql.Connection;
12 | import java.sql.ResultSet;
13 | import java.sql.SQLException;
14 | import java.util.Set;
15 |
16 | /**
17 | * This object manages the persstence for Organization objects, that are mainly
18 | * interesting because they manage an 1:N relationship to Persons.
19 | *
20 | * @author lenz
21 | */
22 | public class OrganizationDB extends ObjectiveFetch {
23 |
24 | /**
25 | * Defines how my table looks like on DB.
26 | *
27 | * I put in all the information I need for updating it.
28 | *
29 | * @return
30 | */
31 |
32 | @Override
33 | public SqlTable table() {
34 | return new SqlTable("org")
35 | .field(SqlField.pk("id", "int auto_increment", null))
36 | .field(SqlField.str("name", "char(50)", null, null));
37 | }
38 |
39 | /**
40 | * Loads an object of class Organization.
41 | *
42 | * Notice how we use a DeferredLoader that holds a reference to the object
43 | * being created so that after we have created objects, we can query the
44 | * database again and load other objects by their PK. This means we can coalesce
45 | * PK accesses.
46 | *
47 | * @param rs
48 | * @return
49 | * @throws SQLException
50 | */
51 |
52 | @Override
53 | public Organization load(ResultSet rs) throws SQLException {
54 | final Organization org = new Organization();
55 | org.id = rs.getInt("id");
56 | org.name = rs.getString("name");
57 |
58 | // we load all kids for this parent
59 | deferLoading( new DeferredLoadByParent() {
60 | @Override
61 | public void update(Set sons) {
62 | org.members = sons;
63 | }
64 | }.setup( new PersonDB(), org.id ));
65 |
66 | return org;
67 |
68 | }
69 |
70 | /**
71 | * Saves an object of class Organization and all its kids.
72 | *
73 | * Note that we currently do not set the PK if it's not set.
74 | *
75 | * @param p The object to be saved
76 | * @param su Th
77 | * e fieldSet where I will set values.
78 | * @throws SQLException
79 | */
80 |
81 | @Override
82 | public void save(Organization p, FieldSet su) throws SQLException {
83 | if (p.id > 0) {
84 | su.set("id", new IntValue(p.id));
85 | }
86 | su.set("name", new StringValue(p.name));
87 | }
88 |
89 | @Override
90 | public void saveSubObjects(Connection conn, Organization obj) throws SQLException {
91 | PersonDB db = new PersonDB();
92 | db.commitAll(conn, obj.members, Integer.toString(obj.id) );
93 | }
94 |
95 | /**
96 | * Updates the PK on insert.
97 | *
98 | * @param pkFromDb
99 | */
100 | @Override
101 | public void updatePrimaryKey(Organization object, String pkFromDb) {
102 | object.id = SqlTools.cint(pkFromDb);
103 | }
104 |
105 |
106 |
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/Person.java:
--------------------------------------------------------------------------------
1 |
2 | package ch.loway.oss.ObjectiveSync.maps;
3 |
4 | /**
5 | *
6 | *
7 | * $Id$
8 | * @author lenz
9 | */
10 | public class Person {
11 |
12 | public int id =0;
13 | public String name = "";
14 | public String surname = "";
15 |
16 | public static Person build( int id, String name, String surn) {
17 | Person p = new Person();
18 | p.id = 0;
19 | p.name = name;
20 | p.surname = surn;
21 | return p;
22 | }
23 |
24 | }
25 |
26 | // $Log$
27 | //
28 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/maps/PersonDB.java:
--------------------------------------------------------------------------------
1 | package ch.loway.oss.ObjectiveSync.maps;
2 |
3 | import ch.loway.oss.ObjectiveSync.ObjectiveFetch;
4 | import ch.loway.oss.ObjectiveSync.SqlTools;
5 | import ch.loway.oss.ObjectiveSync.table.SqlField;
6 | import ch.loway.oss.ObjectiveSync.table.SqlTable;
7 | import ch.loway.oss.ObjectiveSync.table.type.IntValue;
8 | import ch.loway.oss.ObjectiveSync.table.type.StringValue;
9 | import ch.loway.oss.ObjectiveSync.updater.FieldSet;
10 | import java.sql.ResultSet;
11 | import java.sql.SQLException;
12 |
13 | /**
14 | * This object manages the persstence for Person objects.
15 | * You coud create it as an inner static class of Person, but for this example
16 | * it is easier if we just keep it separate.
17 | *
18 | * @author lenz
19 | */
20 | public class PersonDB extends ObjectiveFetch {
21 |
22 | /**
23 | * Defines how my table looks like on DB.
24 | *
25 | * I put in all the information I need for updating it.
26 | *
27 | * @return
28 | */
29 |
30 | @Override
31 | public SqlTable table() {
32 | return new SqlTable("person")
33 | .field(SqlField.pk("id", "int auto_increment", null))
34 | .field(SqlField.str("name", "char(50)", null, null))
35 | .field(SqlField.str("surname", "char(50)", null, null))
36 | .field(SqlField.parentPk("org_id", "int", null))
37 | ;
38 | }
39 |
40 | /**
41 | * Loads an obcet of class Person.
42 | *
43 | * @param rs
44 | * @return
45 | * @throws SQLException
46 | */
47 |
48 | @Override
49 | public Person load(ResultSet rs) throws SQLException {
50 | Person p = new Person();
51 | p.id = rs.getInt("id");
52 | p.name = rs.getString("name");
53 | p.surname = rs.getString("surname");
54 | return p;
55 | }
56 |
57 | /**
58 | * Saves an object of class Person.
59 | * Note that we currently do not set the PK if it's not set.
60 | *
61 | * @param p The object to be saved
62 | * @param su The fieldSet where I will set values.
63 | * @throws SQLException
64 | */
65 |
66 | @Override
67 | public void save(Person p, FieldSet su) throws SQLException {
68 | if (p.id > 0) {
69 | su.set("id", new IntValue(p.id));
70 | }
71 | su.set("name", new StringValue(p.name));
72 | su.set("surname", new StringValue(p.surname));
73 | }
74 |
75 |
76 | /**
77 | * Updates the PK on insert.
78 | *
79 | * @param pkFromDb
80 | */
81 | @Override
82 | public void updatePrimaryKey(Person object, String pkFromDb) {
83 | object.id = SqlTools.cint(pkFromDb);
84 | }
85 |
86 |
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/tests/ch/loway/oss/ObjectiveSync/table/SqlTableTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this template, choose Tools | Templates
3 | * and open the template in the editor.
4 | */
5 |
6 | package ch.loway.oss.ObjectiveSync.table;
7 |
8 | import org.junit.After;
9 | import org.junit.AfterClass;
10 | import org.junit.Before;
11 | import org.junit.BeforeClass;
12 | import org.junit.Test;
13 | import static org.junit.Assert.*;
14 |
15 | /**
16 | *
17 | * @author lenz
18 | */
19 | public class SqlTableTest {
20 |
21 | public SqlTableTest() {
22 | }
23 |
24 | @BeforeClass
25 | public static void setUpClass() throws Exception {
26 | }
27 |
28 | @AfterClass
29 | public static void tearDownClass() throws Exception {
30 | }
31 |
32 | @Before
33 | public void setUp() {
34 | }
35 |
36 | @After
37 | public void tearDown() {
38 | }
39 |
40 | @Test
41 | public void testWritingStyle() {
42 |
43 | SqlTable s = new SqlTable("Hello")
44 | .field( SqlField.pk( "id", "INT(11)", "") )
45 | .field( SqlField.str( "name", "VARCHAR(50)", null, null) )
46 | .field( SqlField.i( "age", "INT(11)", null, null) );
47 |
48 | assertEquals( "PK", "id", s.getPk().name );
49 | }
50 |
51 | }
--------------------------------------------------------------------------------