From 0eebab12eef615ce216cf84971a68f26839250bf Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Thu, 13 Oct 2022 17:48:30 +1100 Subject: [PATCH 01/18] Adding read method to Active-record src --- active-record/src/DB.java | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 active-record/src/DB.java diff --git a/active-record/src/DB.java b/active-record/src/DB.java new file mode 100644 index 000000000000..f5ae309b837f --- /dev/null +++ b/active-record/src/DB.java @@ -0,0 +1,45 @@ +import java.sql.*; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +//import com.mysql.jdbc.Connection; +//import com.mysql.jdbc.PreparedStatement; +//import com.mysql.jdbc.Statement; +import java.sql.*; + +public class DB { + final String dbDomain; + final String dbName; + final String username; + final String password; + final Connection connection; + + public DB(String dbName, String username, String password, String dbDomain) throws SQLException { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.connection = DriverManager.getConnection( + "jdbc:mysql://" + this.dbDomain + "/" +this.dbName, + this.username, + this.password); + } + + + public static void Write() throws SQLException { + + } + public void Read() throws SQLException { + Statement statement = this.connection.createStatement(); + + ResultSet resultSet = statement.executeQuery("select * from people"); + + while (resultSet.next()){ + System.out.println(resultSet.getString("firstname")); + } + + } + public static void Update() throws SQLException { + + } +} From cbcf8c5ff4c96d298d89c17e837e5e59ae12f2d5 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Mon, 24 Oct 2022 13:03:34 +1100 Subject: [PATCH 02/18] Adding main method to active directory and adding row class. --- active-record/src/DB.java | 31 +++++++++++++++++++++++++------ active-record/src/row.java | 9 +++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 active-record/src/row.java diff --git a/active-record/src/DB.java b/active-record/src/DB.java index f5ae309b837f..80fb94b9a633 100644 --- a/active-record/src/DB.java +++ b/active-record/src/DB.java @@ -6,40 +6,59 @@ //import com.mysql.jdbc.PreparedStatement; //import com.mysql.jdbc.Statement; import java.sql.*; +import java.util.ArrayList; public class DB { final String dbDomain; final String dbName; final String username; final String password; + final String tableName; + final String ID; final Connection connection; - public DB(String dbName, String username, String password, String dbDomain) throws SQLException { + public DB(String dbName, String username, String password, String dbDomain, String tableName, String ID) throws SQLException { this.dbDomain = dbDomain; this.dbName = dbName; this.username = username; this.password = password; + this.tableName = tableName; + this.ID = ID; this.connection = DriverManager.getConnection( "jdbc:mysql://" + this.dbDomain + "/" +this.dbName, this.username, this.password); + + } public static void Write() throws SQLException { } - public void Read() throws SQLException { - Statement statement = this.connection.createStatement(); - ResultSet resultSet = statement.executeQuery("select * from people"); + public String Read() throws SQLException { + Statement statement = this.connection.createStatement(); - while (resultSet.next()){ - System.out.println(resultSet.getString("firstname")); + ResultSet resultSet = statement.executeQuery("select * from `world`.`city`"); + ResultSet columnSet = statement.executeQuery("SELECT * FROM `"+this.dbName+"`.`"+this.tableName+"`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + int columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount+1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); } + + return columnNames.get(0); } public static void Update() throws SQLException { } + + public static void main(String[] args) throws SQLException { + DB db = new DB("world","root","apple-trunks","localhost:3306", "city", "1"); + System.out.println(db.Read()); + } } diff --git a/active-record/src/row.java b/active-record/src/row.java new file mode 100644 index 000000000000..b77972010ddd --- /dev/null +++ b/active-record/src/row.java @@ -0,0 +1,9 @@ +import java.util.ArrayList; + +public class row { + ArrayList contents; + + public row(ArrayList contents) { + this.contents = contents; + } +} From 572e6e06841dbb8ee1cb8744acb861d835026eb9 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Mon, 24 Oct 2022 21:14:08 +1100 Subject: [PATCH 03/18] Fixing file system. --- active-record/pom.xml | 12 ++++ active-record/src/DB.java | 64 ------------------- .../java/com/iluwatar/activeobject/App.java | 4 ++ active-record/src/row.java | 9 --- pom.xml | 1 + 5 files changed, 17 insertions(+), 73 deletions(-) create mode 100644 active-record/pom.xml delete mode 100644 active-record/src/DB.java create mode 100644 active-record/src/main/java/com/iluwatar/activeobject/App.java delete mode 100644 active-record/src/row.java diff --git a/active-record/pom.xml b/active-record/pom.xml new file mode 100644 index 000000000000..836f18ac6142 --- /dev/null +++ b/active-record/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + org.example + active-record + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/active-record/src/DB.java b/active-record/src/DB.java deleted file mode 100644 index 80fb94b9a633..000000000000 --- a/active-record/src/DB.java +++ /dev/null @@ -1,64 +0,0 @@ -import java.sql.*; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -//import com.mysql.jdbc.Connection; -//import com.mysql.jdbc.PreparedStatement; -//import com.mysql.jdbc.Statement; -import java.sql.*; -import java.util.ArrayList; - -public class DB { - final String dbDomain; - final String dbName; - final String username; - final String password; - final String tableName; - final String ID; - final Connection connection; - - public DB(String dbName, String username, String password, String dbDomain, String tableName, String ID) throws SQLException { - this.dbDomain = dbDomain; - this.dbName = dbName; - this.username = username; - this.password = password; - this.tableName = tableName; - this.ID = ID; - this.connection = DriverManager.getConnection( - "jdbc:mysql://" + this.dbDomain + "/" +this.dbName, - this.username, - this.password); - - - } - - - public static void Write() throws SQLException { - - } - - public String Read() throws SQLException { - Statement statement = this.connection.createStatement(); - - ResultSet resultSet = statement.executeQuery("select * from `world`.`city`"); - ResultSet columnSet = statement.executeQuery("SELECT * FROM `"+this.dbName+"`.`"+this.tableName+"`"); - ArrayList columnNames = new ArrayList(); - ResultSetMetaData rsmd = columnSet.getMetaData(); - int columnCount = rsmd.getColumnCount(); - for (int i = 1; i < columnCount+1; i++) { - String name = rsmd.getColumnName(i); - columnNames.add(name); - } - - - return columnNames.get(0); - } - public static void Update() throws SQLException { - - } - - public static void main(String[] args) throws SQLException { - DB db = new DB("world","root","apple-trunks","localhost:3306", "city", "1"); - System.out.println(db.Read()); - } -} diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java new file mode 100644 index 000000000000..ee12bc3356e6 --- /dev/null +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -0,0 +1,4 @@ +package com.iluwatar.activeobject; + +public class App { +} diff --git a/active-record/src/row.java b/active-record/src/row.java deleted file mode 100644 index b77972010ddd..000000000000 --- a/active-record/src/row.java +++ /dev/null @@ -1,9 +0,0 @@ -import java.util.ArrayList; - -public class row { - ArrayList contents; - - public row(ArrayList contents) { - this.contents = contents; - } -} diff --git a/pom.xml b/pom.xml index 3aacbad55d4a..f1cefed39d24 100644 --- a/pom.xml +++ b/pom.xml @@ -231,6 +231,7 @@ composite-view metadata-mapping service-to-worker + active-record From f9a3d948ca037f384938e4a220c7757be236089b Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Mon, 24 Oct 2022 21:18:00 +1100 Subject: [PATCH 04/18] Added delete, write, and read methods to ActiveRow --- active-record/pom.xml | 44 +++++++-- .../com/iluwatar/activeobject/ActiveRow.java | 94 +++++++++++++++++++ .../java/com/iluwatar/activeobject/DB.java | 37 ++++++++ .../com/iluwatar/activeobject/Rowverride.java | 10 ++ 4 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java create mode 100644 active-record/src/main/java/com/iluwatar/activeobject/DB.java create mode 100644 active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java diff --git a/active-record/pom.xml b/active-record/pom.xml index 836f18ac6142..359bdede90d0 100644 --- a/active-record/pom.xml +++ b/active-record/pom.xml @@ -1,12 +1,44 @@ - + 4.0.0 - - org.example + + java-design-patterns + com.iluwatar + 1.26.0-SNAPSHOT + active-record - 1.0-SNAPSHOT + + + org.junit.jupiter + junit-jupiter-engine + test + + + + 15 + 15 + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + active-record + + + + + + + + \ No newline at end of file diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java new file mode 100644 index 000000000000..f5bb782ebb09 --- /dev/null +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -0,0 +1,94 @@ +package com.iluwatar.activeobject; +import java.sql.*; +import java.util.ArrayList; + +/** + * ActiveRow attempts to implement the fundamental ruby on rails 'Active Record' design pattern in Java. + */ +public class ActiveRow { + String ID; + String IDName; + DB dataBase; + ArrayList columns; + ArrayList contents = new ArrayList(); + + /** + * @param dataBase A Database object which handles opening the connection. + * @param ID The unique identifier of a row. + * @throws SQLException + */ + public ActiveRow(DB dataBase, String ID) throws SQLException { + this.ID = ID; + Statement statement = dataBase.connection.createStatement(); + ResultSet columnSet = statement.executeQuery("SELECT * FROM `"+dataBase.dbName+"`.`"+dataBase.tableName+"`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + int columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount+1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); + } + this.columns = columnNames; + ResultSet rowResult = statement.executeQuery("SELECT * FROM `"+dataBase.dbName+"`.`"+dataBase.tableName+"` WHERE ID="+this.ID); + while (rowResult.next()) + { + for (int col=1; col <= columnCount; col++) + { + Object value = rowResult.getObject(col); + if (value != null) { + contents.add(value.toString()); + } + } + } + rowResult.close(); + statement.close(); + columnSet.close(); + dataBase.connection.close(); + } + + + /** + * + * @throws SQLException + */ + @Rowverride + public void Write() throws SQLException { + StringBuilder query = new StringBuilder(); + query.append("INSERT INTO `").append(dataBase.dbName).append("`.`").append(dataBase.tableName).append("`("); + for (String col: this.columns){ + if(columns.indexOf(col) != col.length()){ + query.append(col); + query.append(","); + } else{ + query.append(col); + query.append(") VALUES ("); + } + } + for (String con: this.contents){ + if(columns.indexOf(con) != con.length()){ + query.append(con); + query.append(","); + } else{ + query.append(con); + query.append(")"); + } + } + PreparedStatement stmt = dataBase.connection.prepareStatement(query.toString()); + stmt.executeUpdate(); + } + + @Rowverride + public void Delete() throws SQLException { + PreparedStatement st = dataBase.connection.prepareStatement("DELETE FROM `"+dataBase.dbName+"`.`"+dataBase.tableName +"`" + " WHERE ID = '" + this.ID + "';"); + } + + @Rowverride + public ArrayList Read() throws SQLException { + Connection c = DriverManager.getConnection( + "jdbc:mysql://" + dataBase.dbDomain + "/" +dataBase.dbName, + dataBase.username, + dataBase.password); + return this.contents; + } + +} diff --git a/active-record/src/main/java/com/iluwatar/activeobject/DB.java b/active-record/src/main/java/com/iluwatar/activeobject/DB.java new file mode 100644 index 000000000000..430aa3e8598b --- /dev/null +++ b/active-record/src/main/java/com/iluwatar/activeobject/DB.java @@ -0,0 +1,37 @@ +package com.iluwatar.activeobject; +import java.sql.*; +import java.sql.DriverManager; +import java.sql.SQLException; +//import com.mysql.jdbc.Connection; +//import com.mysql.jdbc.PreparedStatement; +//import com.mysql.jdbc.Statement; +import java.util.ArrayList; + +public class DB { + final String dbDomain; + final String dbName; + final String username; + final String password; + final String tableName; + final Connection connection; + + public DB(String dbName, String username, String password, String dbDomain, String tableName) throws SQLException { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; + this.connection = DriverManager.getConnection( + "jdbc:mysql://" + this.dbDomain + "/" +this.dbName, + this.username, + this.password); + } + + public static void main(String[] args) throws SQLException { + DB db = new DB("world","root","apple-trunks","localhost:3306", "city"); + ActiveRow activeRow = new ActiveRow(db, "3"); + ArrayList s = (activeRow.Read()); + System.out.println(s); + + } +} diff --git a/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java new file mode 100644 index 000000000000..0e7ec631e69c --- /dev/null +++ b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java @@ -0,0 +1,10 @@ +package com.iluwatar.activeobject; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Rowverride { +} From deebc13255d7dab818a1b6020355a48d58b4663a Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Thu, 27 Oct 2022 18:21:50 +1100 Subject: [PATCH 05/18] Checkstyle changes. --- active-record/README.md | 0 active-record/pom.xml | 5 + .../com/iluwatar/activeobject/ActiveRow.java | 170 ++++++++++-------- .../java/com/iluwatar/activeobject/App.java | 11 ++ .../java/com/iluwatar/activeobject/DB.java | 52 +++--- .../src/test/java/ActiveRowTest.java | 8 + active-record/src/test/java/AppTest.java | 10 ++ 7 files changed, 154 insertions(+), 102 deletions(-) create mode 100644 active-record/README.md create mode 100644 active-record/src/test/java/ActiveRowTest.java create mode 100644 active-record/src/test/java/AppTest.java diff --git a/active-record/README.md b/active-record/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/active-record/pom.xml b/active-record/pom.xml index 359bdede90d0..9661de1449ab 100644 --- a/active-record/pom.xml +++ b/active-record/pom.xml @@ -8,6 +8,11 @@ active-record + + com.mysql + mysql-connector-j + 8.0.31 + org.junit.jupiter junit-jupiter-engine diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index f5bb782ebb09..8d7d8a0b0900 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -1,94 +1,106 @@ package com.iluwatar.activeobject; -import java.sql.*; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; /** - * ActiveRow attempts to implement the fundamental ruby on rails 'Active Record' design pattern in Java. + * ActiveRow attempts to implement the fundamental ruby on rails 'Active Record' design pattern in + * Java. */ public class ActiveRow { - String ID; - String IDName; - DB dataBase; - ArrayList columns; - ArrayList contents = new ArrayList(); - - /** - * @param dataBase A Database object which handles opening the connection. - * @param ID The unique identifier of a row. - * @throws SQLException - */ - public ActiveRow(DB dataBase, String ID) throws SQLException { - this.ID = ID; - Statement statement = dataBase.connection.createStatement(); - ResultSet columnSet = statement.executeQuery("SELECT * FROM `"+dataBase.dbName+"`.`"+dataBase.tableName+"`"); - ArrayList columnNames = new ArrayList(); - ResultSetMetaData rsmd = columnSet.getMetaData(); - int columnCount = rsmd.getColumnCount(); - for (int i = 1; i < columnCount+1; i++) { - String name = rsmd.getColumnName(i); - columnNames.add(name); - } - this.columns = columnNames; - ResultSet rowResult = statement.executeQuery("SELECT * FROM `"+dataBase.dbName+"`.`"+dataBase.tableName+"` WHERE ID="+this.ID); - while (rowResult.next()) - { - for (int col=1; col <= columnCount; col++) - { - Object value = rowResult.getObject(col); - if (value != null) { - contents.add(value.toString()); - } - } - } - rowResult.close(); - statement.close(); - columnSet.close(); - dataBase.connection.close(); - } + String ID; + Connection con; + String READ; + String DELETE; + String WRITE; + DB dataBase; + int columnCount; + ArrayList columns; + ArrayList contents = new ArrayList(); - /** - * - * @throws SQLException - */ - @Rowverride - public void Write() throws SQLException { - StringBuilder query = new StringBuilder(); - query.append("INSERT INTO `").append(dataBase.dbName).append("`.`").append(dataBase.tableName).append("`("); - for (String col: this.columns){ - if(columns.indexOf(col) != col.length()){ - query.append(col); - query.append(","); - } else{ - query.append(col); - query.append(") VALUES ("); - } - } - for (String con: this.contents){ - if(columns.indexOf(con) != con.length()){ - query.append(con); - query.append(","); - } else{ - query.append(con); - query.append(")"); - } - } - PreparedStatement stmt = dataBase.connection.prepareStatement(query.toString()); - stmt.executeUpdate(); + /** + * @param dataBase A Database object which handles opening the connection. + * @param ID The unique identifier of a row. + * @throws SQLException + */ + public ActiveRow(DB dataBase, String ID) throws SQLException { + this.ID = ID; + con = DriverManager + .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), + dataBase.getUsername(), dataBase.getPassword()); + Statement statement = con.createStatement(); + ResultSet columnSet = statement.executeQuery( + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount + 1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); } + this.columns = columnNames; + READ = + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" + + this.ID; + DELETE = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" + + " WHERE ID = '" + this.ID + "';"; + WRITE = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; + statement.close(); + columnSet.close(); + } - @Rowverride - public void Delete() throws SQLException { - PreparedStatement st = dataBase.connection.prepareStatement("DELETE FROM `"+dataBase.dbName+"`.`"+dataBase.tableName +"`" + " WHERE ID = '" + this.ID + "';"); + @Rowverride + public void Write() throws SQLException { + StringBuilder query = new StringBuilder(); + query.append(WRITE); + query.append(" VALUES ("); + for (String con : this.contents) { + if (contents.indexOf(con) != this.contents.size() - 1) { + query.append("'"); + query.append(con); + query.append("' "); + query.append(","); + } else { + query.append("'"); + query.append(con); + query.append("' "); + query.append(")"); + } } + System.out.println(query); + PreparedStatement stmt = con.prepareStatement(query.toString()); + stmt.executeUpdate(); + + } + + @Rowverride + public void Delete() throws SQLException { + con.prepareStatement(DELETE).executeUpdate(); + } - @Rowverride - public ArrayList Read() throws SQLException { - Connection c = DriverManager.getConnection( - "jdbc:mysql://" + dataBase.dbDomain + "/" +dataBase.dbName, - dataBase.username, - dataBase.password); - return this.contents; + @Rowverride + public ArrayList Read() throws SQLException { + Statement statement = con.createStatement(); + + ResultSet rowResult = statement.executeQuery(READ); + while (rowResult.next()) { + for (int col = 1; col <= columnCount; col++) { + Object value = rowResult.getObject(col); + if (value != null) { + contents.add(value.toString()); + } + } } + rowResult.close(); + + return this.contents; + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java index ee12bc3356e6..4048238c5226 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/App.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -1,4 +1,15 @@ package com.iluwatar.activeobject; +import java.sql.SQLException; + public class App { + public static void main(String[] args) { + try{ + DB db = new DB("world","root","apple-trunks","localhost:3306", "city"); + + } catch (SQLException e){ + e.printStackTrace(); + } + + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/DB.java b/active-record/src/main/java/com/iluwatar/activeobject/DB.java index 430aa3e8598b..2e2d9c0f8999 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/DB.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/DB.java @@ -2,36 +2,42 @@ import java.sql.*; import java.sql.DriverManager; import java.sql.SQLException; -//import com.mysql.jdbc.Connection; -//import com.mysql.jdbc.PreparedStatement; -//import com.mysql.jdbc.Statement; import java.util.ArrayList; +import java.util.Arrays; public class DB { - final String dbDomain; - final String dbName; - final String username; - final String password; - final String tableName; - final Connection connection; + final String dbDomain; + final String dbName; + final String username; + final String password; + final String tableName; public DB(String dbName, String username, String password, String dbDomain, String tableName) throws SQLException { - this.dbDomain = dbDomain; - this.dbName = dbName; - this.username = username; - this.password = password; - this.tableName = tableName; - this.connection = DriverManager.getConnection( - "jdbc:mysql://" + this.dbDomain + "/" +this.dbName, - this.username, - this.password); + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; } - public static void main(String[] args) throws SQLException { - DB db = new DB("world","root","apple-trunks","localhost:3306", "city"); - ActiveRow activeRow = new ActiveRow(db, "3"); - ArrayList s = (activeRow.Read()); - System.out.println(s); + public String getDbDomain() { + return dbDomain; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + public String getTableName() { + return tableName; } + } diff --git a/active-record/src/test/java/ActiveRowTest.java b/active-record/src/test/java/ActiveRowTest.java new file mode 100644 index 000000000000..bf24bf741780 --- /dev/null +++ b/active-record/src/test/java/ActiveRowTest.java @@ -0,0 +1,8 @@ +import com.iluwatar.activeobject.App; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + + +public class ActiveRowTest { + +} diff --git a/active-record/src/test/java/AppTest.java b/active-record/src/test/java/AppTest.java new file mode 100644 index 000000000000..a0f83bf97b42 --- /dev/null +++ b/active-record/src/test/java/AppTest.java @@ -0,0 +1,10 @@ +import com.iluwatar.activeobject.App; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +class AppTest { + @Test + void runWithoutException() { + assertDoesNotThrow(()-> App.main(new String[]{})); + } +} \ No newline at end of file From dead83bfec6fabd79485c9e86808fc051af1800f Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Thu, 27 Oct 2022 18:41:28 +1100 Subject: [PATCH 06/18] Checkstyle done basically, working on tests. --- .../com/iluwatar/activeobject/ActiveRow.java | 46 ++++++++------ .../java/com/iluwatar/activeobject/App.java | 13 ++-- .../java/com/iluwatar/activeobject/DB.java | 61 +++++++++---------- .../src/test/java/ActiveRowTest.java | 8 --- active-record/src/test/java/AppTest.java | 10 +-- .../iluwatar/activeobject/ActiveRowTest.java | 36 +++++++++++ 6 files changed, 105 insertions(+), 69 deletions(-) delete mode 100644 active-record/src/test/java/ActiveRowTest.java create mode 100644 active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index 8d7d8a0b0900..3274c5b5d56b 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -15,23 +15,29 @@ */ public class ActiveRow { - String ID; - Connection con; - String READ; - String DELETE; - String WRITE; + String id; DB dataBase; + Connection con; + String read; + String delete; + String write; int columnCount; ArrayList columns; - ArrayList contents = new ArrayList(); + ArrayList contents = new ArrayList<>(); /** * @param dataBase A Database object which handles opening the connection. - * @param ID The unique identifier of a row. + * @param id The unique identifier of a row. * @throws SQLException */ - public ActiveRow(DB dataBase, String ID) throws SQLException { - this.ID = ID; + public ActiveRow(DB dataBase, String id) throws SQLException { + this.dataBase = dataBase; + this.id = id; + initialise(); + } + + @Rowverride + public void initialise() throws SQLException { con = DriverManager .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), dataBase.getUsername(), dataBase.getPassword()); @@ -46,20 +52,20 @@ public ActiveRow(DB dataBase, String ID) throws SQLException { columnNames.add(name); } this.columns = columnNames; - READ = + read = "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" - + this.ID; - DELETE = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" - + " WHERE ID = '" + this.ID + "';"; - WRITE = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; + + this.id; + delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" + + " WHERE ID = '" + this.id + "';"; + write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; statement.close(); columnSet.close(); } @Rowverride - public void Write() throws SQLException { + public void write() throws SQLException { StringBuilder query = new StringBuilder(); - query.append(WRITE); + query.append(write); query.append(" VALUES ("); for (String con : this.contents) { if (contents.indexOf(con) != this.contents.size() - 1) { @@ -81,15 +87,15 @@ public void Write() throws SQLException { } @Rowverride - public void Delete() throws SQLException { - con.prepareStatement(DELETE).executeUpdate(); + public void delete() throws SQLException { + con.prepareStatement(delete).executeUpdate(); } @Rowverride - public ArrayList Read() throws SQLException { + public ArrayList read() throws SQLException { Statement statement = con.createStatement(); - ResultSet rowResult = statement.executeQuery(READ); + ResultSet rowResult = statement.executeQuery(read); while (rowResult.next()) { for (int col = 1; col <= columnCount; col++) { Object value = rowResult.getObject(col); diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java index 4048238c5226..3c06f25ceb88 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/App.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -3,13 +3,14 @@ import java.sql.SQLException; public class App { - public static void main(String[] args) { - try{ - DB db = new DB("world","root","apple-trunks","localhost:3306", "city"); - } catch (SQLException e){ - e.printStackTrace(); - } + public static void main(String[] args) { + try { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + } catch (SQLException e) { + e.printStackTrace(); } + + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/DB.java b/active-record/src/main/java/com/iluwatar/activeobject/DB.java index 2e2d9c0f8999..c0abd42e3ee7 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/DB.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/DB.java @@ -1,43 +1,42 @@ package com.iluwatar.activeobject; -import java.sql.*; -import java.sql.DriverManager; + import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; public class DB { + final String dbDomain; final String dbName; final String username; final String password; final String tableName; - public DB(String dbName, String username, String password, String dbDomain, String tableName) throws SQLException { - this.dbDomain = dbDomain; - this.dbName = dbName; - this.username = username; - this.password = password; - this.tableName = tableName; - } - - public String getDbDomain() { - return dbDomain; - } - - public String getDbName() { - return dbName; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getTableName() { - return tableName; - } + public DB(String dbName, String username, String password, String dbDomain, String tableName) + throws SQLException { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; + } + + public String getDbDomain() { + return dbDomain; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTableName() { + return tableName; + } } diff --git a/active-record/src/test/java/ActiveRowTest.java b/active-record/src/test/java/ActiveRowTest.java deleted file mode 100644 index bf24bf741780..000000000000 --- a/active-record/src/test/java/ActiveRowTest.java +++ /dev/null @@ -1,8 +0,0 @@ -import com.iluwatar.activeobject.App; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - - -public class ActiveRowTest { - -} diff --git a/active-record/src/test/java/AppTest.java b/active-record/src/test/java/AppTest.java index a0f83bf97b42..5eee004f6931 100644 --- a/active-record/src/test/java/AppTest.java +++ b/active-record/src/test/java/AppTest.java @@ -1,10 +1,12 @@ import com.iluwatar.activeobject.App; import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; class AppTest { - @Test - void runWithoutException() { - assertDoesNotThrow(()-> App.main(new String[]{})); - } + + @Test + void runWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } } \ No newline at end of file diff --git a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java new file mode 100644 index 000000000000..b58db4e17e87 --- /dev/null +++ b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java @@ -0,0 +1,36 @@ +package com.iluwatar.activeobject; + +import com.iluwatar.activeobject.App; +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + + +public class ActiveRowTest { + + @Test + void initialiseTest() throws SQLException { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(db, "1"); + assertDoesNotThrow(row::initialise); + } + + + @Test + void readRowTest() throws SQLException { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(db, "1"); + + } + + @Test + void deleteRowTest() { + + } + +@Test + void insertRowTest() { + + } + +} From 9dae29cad82809543b47aee56171a9be99fde8a0 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Sun, 30 Oct 2022 20:00:52 +1100 Subject: [PATCH 07/18] Checkstyle done basically, working on tests. --- .../com/iluwatar/activeobject/ActiveRow.java | 216 ++++++++++-------- .../java/com/iluwatar/activeobject/App.java | 21 +- .../java/com/iluwatar/activeobject/DB.java | 67 +++--- .../com/iluwatar/activeobject/Rowverride.java | 2 + .../iluwatar/activeobject/ActiveRowTest.java | 34 ++- .../iluwatar/activeobject}/AppTest.java | 10 +- 6 files changed, 204 insertions(+), 146 deletions(-) rename active-record/src/test/java/{ => com/iluwatar/activeobject}/AppTest.java (55%) diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index 3274c5b5d56b..5fbb6e315d1a 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -6,6 +6,7 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.SQLIntegrityConstraintViolationException; import java.sql.Statement; import java.util.ArrayList; @@ -15,98 +16,127 @@ */ public class ActiveRow { - String id; - DB dataBase; - Connection con; - String read; - String delete; - String write; - int columnCount; - ArrayList columns; - ArrayList contents = new ArrayList<>(); - - /** - * @param dataBase A Database object which handles opening the connection. - * @param id The unique identifier of a row. - * @throws SQLException - */ - public ActiveRow(DB dataBase, String id) throws SQLException { - this.dataBase = dataBase; - this.id = id; - initialise(); - } - - @Rowverride - public void initialise() throws SQLException { - con = DriverManager - .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), - dataBase.getUsername(), dataBase.getPassword()); - Statement statement = con.createStatement(); - ResultSet columnSet = statement.executeQuery( - "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); - ArrayList columnNames = new ArrayList(); - ResultSetMetaData rsmd = columnSet.getMetaData(); - columnCount = rsmd.getColumnCount(); - for (int i = 1; i < columnCount + 1; i++) { - String name = rsmd.getColumnName(i); - columnNames.add(name); - } - this.columns = columnNames; - read = - "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" - + this.id; - delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" - + " WHERE ID = '" + this.id + "';"; - write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; - statement.close(); - columnSet.close(); - } - - @Rowverride - public void write() throws SQLException { - StringBuilder query = new StringBuilder(); - query.append(write); - query.append(" VALUES ("); - for (String con : this.contents) { - if (contents.indexOf(con) != this.contents.size() - 1) { - query.append("'"); - query.append(con); - query.append("' "); - query.append(","); - } else { - query.append("'"); - query.append(con); - query.append("' "); - query.append(")"); - } - } - System.out.println(query); - PreparedStatement stmt = con.prepareStatement(query.toString()); - stmt.executeUpdate(); - - } - - @Rowverride - public void delete() throws SQLException { - con.prepareStatement(delete).executeUpdate(); - } - - @Rowverride - public ArrayList read() throws SQLException { - Statement statement = con.createStatement(); - - ResultSet rowResult = statement.executeQuery(read); - while (rowResult.next()) { - for (int col = 1; col <= columnCount; col++) { - Object value = rowResult.getObject(col); - if (value != null) { - contents.add(value.toString()); - } - } - } - rowResult.close(); - - return this.contents; - } + String id; + DB dataBase; + Connection con; + String read; + String delete; + String write; + int columnCount; + ArrayList columns; + ArrayList contents = new ArrayList<>(); + + /** + * @param dataBase A Database object which handles opening the connection. + * @param id The unique identifier of a row. + */ + public ActiveRow(DB dataBase, String id) { + this.dataBase = dataBase; + this.id = id; + initialise(); + } + + @Rowverride + public void initialise() { + try { + con = DriverManager + .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), + dataBase.getUsername(), dataBase.getPassword()); + } catch (SQLException e) { + e.printStackTrace(); + } + + try { + Statement statement = con.createStatement(); + ResultSet columnSet = statement.executeQuery( + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount + 1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); + } + this.columns = columnNames; + read = + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" + + this.id; + delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" + + " WHERE ID = '" + this.id + "';"; + write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; + statement.close(); + columnSet.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + + } + + @Rowverride + public void write() { + StringBuilder query = new StringBuilder(); + query.append(write); + query.append(" VALUES ("); + for (String con : this.contents) { + if (contents.indexOf(con) != this.contents.size() - 1) { + query.append("'"); + query.append(con); + query.append("' "); + query.append(","); + } else { + query.append("'"); + query.append(con); + query.append("' "); + query.append(")"); + } + } + + try { + PreparedStatement stmt = con.prepareStatement(query.toString()); + stmt.executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Rowverride + public void delete() { + if (this.read().equals(new ArrayList())) { + System.out.println("Row does not exist."); + return; + } + try { + con.prepareStatement(delete).executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Rowverride + public ArrayList read() { + try { + Statement statement = con.createStatement(); + + ResultSet rowResult = statement.executeQuery(read); + while (rowResult.next()) { + for (int col = 1; col <= columnCount; col++) { + Object value = rowResult.getObject(col); + if (value != null) { + contents.add(value.toString()); + } + } + } + rowResult.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + + return this.contents; + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java index 3c06f25ceb88..b3d9c0307e3a 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/App.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -1,16 +1,23 @@ package com.iluwatar.activeobject; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; public class App { - public static void main(String[] args) { - try { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + public static void main(String[] args) { + try { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow ar = new ActiveRow(db, "101"); + ar.contents = new ArrayList( + Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); + ar.write(); + System.out.println(ar.read()); - } catch (SQLException e) { - e.printStackTrace(); - } + } catch (Exception e) { + e.printStackTrace(); + } - } + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/DB.java b/active-record/src/main/java/com/iluwatar/activeobject/DB.java index c0abd42e3ee7..b51b59c3ff86 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/DB.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/DB.java @@ -4,39 +4,38 @@ public class DB { - final String dbDomain; - final String dbName; - final String username; - final String password; - final String tableName; - - public DB(String dbName, String username, String password, String dbDomain, String tableName) - throws SQLException { - this.dbDomain = dbDomain; - this.dbName = dbName; - this.username = username; - this.password = password; - this.tableName = tableName; - } - - public String getDbDomain() { - return dbDomain; - } - - public String getDbName() { - return dbName; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getTableName() { - return tableName; - } + final String dbDomain; + final String dbName; + final String username; + final String password; + final String tableName; + + public DB(String dbName, String username, String password, String dbDomain, String tableName) { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; + } + + public String getDbDomain() { + return dbDomain; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTableName() { + return tableName; + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java index 0e7ec631e69c..2088a308e4b5 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java @@ -1,4 +1,5 @@ package com.iluwatar.activeobject; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -7,4 +8,5 @@ @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Rowverride { + } diff --git a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java index b58db4e17e87..3fcc46ba5653 100644 --- a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java @@ -1,35 +1,53 @@ package com.iluwatar.activeobject; -import com.iluwatar.activeobject.App; -import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class ActiveRowTest { @Test - void initialiseTest() throws SQLException { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow row = new ActiveRow(db, "1"); - assertDoesNotThrow(row::initialise); + void initialiseTest() { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(db, "1"); + assertDoesNotThrow(row::initialise); } @Test - void readRowTest() throws SQLException { + void readRowTest() { DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); ActiveRow row = new ActiveRow(db, "1"); + if (!(row.read().equals(new ArrayList()))) { + for (int i = 0; i < row.read().size(); i++) { + assertNotNull(row.read().get(i)); + } + } } @Test void deleteRowTest() { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(db, "1"); + row.delete(); + assertEquals(row.read(), new ArrayList()); } -@Test + @Test void insertRowTest() { + DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(db, "1"); + row.delete(); + row.contents = new ArrayList( + Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); + assertEquals(row.read(), Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); } diff --git a/active-record/src/test/java/AppTest.java b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java similarity index 55% rename from active-record/src/test/java/AppTest.java rename to active-record/src/test/java/com/iluwatar/activeobject/AppTest.java index 5eee004f6931..92d44b4e578b 100644 --- a/active-record/src/test/java/AppTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java @@ -1,3 +1,5 @@ +package com.iluwatar.activeobject; + import com.iluwatar.activeobject.App; import org.junit.jupiter.api.Test; @@ -5,8 +7,8 @@ class AppTest { - @Test - void runWithoutException() { - assertDoesNotThrow(() -> App.main(new String[]{})); - } + @Test + void runWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } } \ No newline at end of file From 02440c816643240ca7c1b0a168c557a435d0db8d Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Sun, 30 Oct 2022 20:14:31 +1100 Subject: [PATCH 08/18] Checkstyle done basically, working on tests. --- .../src/main/java/com/iluwatar/activeobject/ActiveRow.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index 5fbb6e315d1a..9bb921b32043 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -6,7 +6,6 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.sql.SQLIntegrityConstraintViolationException; import java.sql.Statement; import java.util.ArrayList; @@ -15,7 +14,6 @@ * Java. */ public class ActiveRow { - String id; DB dataBase; Connection con; From 542858c983fa47e61e9d450ab5be1300e7c90506 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Sun, 30 Oct 2022 20:45:52 +1100 Subject: [PATCH 09/18] Finished, ready to pullrequest. --- active-record/etc/active-record.urm.puml | 40 +++ .../iluwatar/activeobject/ActiveDatabase.java | 55 ++++ .../com/iluwatar/activeobject/ActiveRow.java | 263 ++++++++++-------- .../java/com/iluwatar/activeobject/App.java | 33 ++- .../java/com/iluwatar/activeobject/DB.java | 41 --- .../iluwatar/activeobject/ActiveRowTest.java | 20 +- .../com/iluwatar/activeobject/AppTest.java | 1 - 7 files changed, 267 insertions(+), 186 deletions(-) create mode 100644 active-record/etc/active-record.urm.puml create mode 100644 active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java delete mode 100644 active-record/src/main/java/com/iluwatar/activeobject/DB.java diff --git a/active-record/etc/active-record.urm.puml b/active-record/etc/active-record.urm.puml new file mode 100644 index 000000000000..d7b98c2734a0 --- /dev/null +++ b/active-record/etc/active-record.urm.puml @@ -0,0 +1,40 @@ +@startuml +package com.iluwatar.activeobject { + class ActiveDatabase { + ~ dbDomain : String + ~ dbName : String + ~ password : String + ~ tableName : String + ~ username : String + + ActiveDatabase(dbName : String, username : String, password : String, dbDomain : String, tableName : String) + + getDbDomain() : String + + getDbName() : String + + getPassword() : String + + getTableName() : String + + getUsername() : String + } + class ActiveRow { + ~ columnCount : int + ~ columns : ArrayList + ~ con : Connection + ~ contents : ArrayList + ~ dataBase : ActiveDatabase + ~ delete : String + ~ id : String + ~ read : String + ~ write : String + + ActiveRow(dataBase : ActiveDatabase, id : String) + + delete() + + initialise() + + read() : ArrayList + + write() + } + class App { + + App() + + main(args : String[]) {static} + } + interface Rowverride { + } +} +ActiveRow --> "-dataBase" ActiveDatabase +@enduml \ No newline at end of file diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java new file mode 100644 index 000000000000..0646d43dc98a --- /dev/null +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java @@ -0,0 +1,55 @@ +package com.iluwatar.activeobject; + +import java.sql.SQLException; + +/** + * This class initialises frequently needed variables for the MySQLWorkbench Database. + */ +public class ActiveDatabase { + + final String dbDomain; + final String dbName; + final String username; + final String password; + final String tableName; + + /** + * Constructor needed to instantiate the database object which is used with the MySQLWorkbench + * Database. + * + * @param dbName Name of the database as String. + * @param username Username for the database as String. + * @param password Password for the database as String. + * @param dbDomain The domain for the database as String. Tested on localhost:3306 + * @param tableName Name of the table which you wish to create the active row. + */ + public ActiveDatabase(String dbName, String username, String password, String dbDomain, + String tableName) { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; + } + + public String getDbDomain() { + return dbDomain; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTableName() { + return tableName; + } + +} diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index 9bb921b32043..24b048583a19 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -14,127 +14,146 @@ * Java. */ public class ActiveRow { - String id; - DB dataBase; - Connection con; - String read; - String delete; - String write; - int columnCount; - ArrayList columns; - ArrayList contents = new ArrayList<>(); - - /** - * @param dataBase A Database object which handles opening the connection. - * @param id The unique identifier of a row. - */ - public ActiveRow(DB dataBase, String id) { - this.dataBase = dataBase; - this.id = id; - initialise(); - } - - @Rowverride - public void initialise() { - try { - con = DriverManager - .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), - dataBase.getUsername(), dataBase.getPassword()); - } catch (SQLException e) { - e.printStackTrace(); - } - - try { - Statement statement = con.createStatement(); - ResultSet columnSet = statement.executeQuery( - "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); - ArrayList columnNames = new ArrayList(); - ResultSetMetaData rsmd = columnSet.getMetaData(); - columnCount = rsmd.getColumnCount(); - for (int i = 1; i < columnCount + 1; i++) { - String name = rsmd.getColumnName(i); - columnNames.add(name); - } - this.columns = columnNames; - read = - "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" - + this.id; - delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" - + " WHERE ID = '" + this.id + "';"; - write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; - statement.close(); - columnSet.close(); - } catch (SQLException e) { - e.printStackTrace(); - } - - - } - - @Rowverride - public void write() { - StringBuilder query = new StringBuilder(); - query.append(write); - query.append(" VALUES ("); - for (String con : this.contents) { - if (contents.indexOf(con) != this.contents.size() - 1) { - query.append("'"); - query.append(con); - query.append("' "); - query.append(","); - } else { - query.append("'"); - query.append(con); - query.append("' "); - query.append(")"); - } - } - - try { - PreparedStatement stmt = con.prepareStatement(query.toString()); - stmt.executeUpdate(); - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Rowverride - public void delete() { - if (this.read().equals(new ArrayList())) { - System.out.println("Row does not exist."); - return; - } - try { - con.prepareStatement(delete).executeUpdate(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Rowverride - public ArrayList read() { - try { - Statement statement = con.createStatement(); - - ResultSet rowResult = statement.executeQuery(read); - while (rowResult.next()) { - for (int col = 1; col <= columnCount; col++) { - Object value = rowResult.getObject(col); - if (value != null) { - contents.add(value.toString()); - } - } - } - rowResult.close(); - - } catch (Exception e) { - e.printStackTrace(); - } - - return this.contents; - } + + String id; + ActiveDatabase dataBase; + Connection con; + String read; + String delete; + String write; + int columnCount; + ArrayList columns; + ArrayList contents = new ArrayList<>(); + + /** + * ActiveRow attempts to implement the fundamental ruby on rails 'Active Record' design pattern in + * Java. + * + * @param dataBase A Database object which handles opening the connection. + * @param id The unique identifier of a row. + */ + public ActiveRow(ActiveDatabase dataBase, String id) { + this.dataBase = dataBase; + this.id = id; + initialise(); + } + + /** + * This initialises the class by creating a connection and populating the column names and other + * variables which are used in the program. + */ + @Rowverride + public void initialise() { + try { + con = DriverManager + .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), + dataBase.getUsername(), dataBase.getPassword()); + } catch (SQLException e) { + e.printStackTrace(); + } + + try { + Statement statement = con.createStatement(); + ResultSet columnSet = statement.executeQuery( + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount + 1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); + } + this.columns = columnNames; + read = + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" + + this.id; + delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" + + " WHERE ID = '" + this.id + "';"; + write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; + statement.close(); + columnSet.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + + } + + /** + * Writes contents of the active row object to the chosen database. + */ + @Rowverride + public void write() { + StringBuilder query = new StringBuilder(); + query.append(write); + query.append(" VALUES ("); + for (String con : this.contents) { + if (contents.indexOf(con) != this.contents.size() - 1) { + query.append("'"); + query.append(con); + query.append("' "); + query.append(","); + } else { + query.append("'"); + query.append(con); + query.append("' "); + query.append(")"); + } + } + + try { + PreparedStatement stmt = con.prepareStatement(query.toString()); + stmt.executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Deletes the current instance of the active row from the database based on the given ID value. + */ + @Rowverride + public void delete() { + if (this.read().equals(new ArrayList())) { + System.out.println("Row does not exist."); + return; + } + try { + con.prepareStatement(delete).executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Reads the current active row. + * + * @return the current active row contents as Arraylist + */ + @Rowverride + public ArrayList read() { + try { + Statement statement = con.createStatement(); + + ResultSet rowResult = statement.executeQuery(read); + while (rowResult.next()) { + for (int col = 1; col <= columnCount; col++) { + Object value = rowResult.getObject(col); + if (value != null) { + contents.add(value.toString()); + } + } + } + rowResult.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + + return this.contents; + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java index b3d9c0307e3a..2266e25a1412 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/App.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -1,23 +1,30 @@ package com.iluwatar.activeobject; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +/** + * Useful examples of common useage of the program. + */ public class App { - public static void main(String[] args) { - try { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow ar = new ActiveRow(db, "101"); - ar.contents = new ArrayList( - Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); - ar.write(); - System.out.println(ar.read()); + /** + * Use this method to become familiar with the program. + * + * @param args arguments passed into the main method. + */ + public static void main(String[] args) { + try { + ActiveDatabase activeDatabase = new ActiveDatabase("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow ar = new ActiveRow(activeDatabase, "101"); + ar.contents = new ArrayList( + Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); + ar.write(); + System.out.println(ar.read()); - } catch (Exception e) { - e.printStackTrace(); - } + } catch (Exception e) { + e.printStackTrace(); + } - } + } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/DB.java b/active-record/src/main/java/com/iluwatar/activeobject/DB.java deleted file mode 100644 index b51b59c3ff86..000000000000 --- a/active-record/src/main/java/com/iluwatar/activeobject/DB.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.iluwatar.activeobject; - -import java.sql.SQLException; - -public class DB { - - final String dbDomain; - final String dbName; - final String username; - final String password; - final String tableName; - - public DB(String dbName, String username, String password, String dbDomain, String tableName) { - this.dbDomain = dbDomain; - this.dbName = dbName; - this.username = username; - this.password = password; - this.tableName = tableName; - } - - public String getDbDomain() { - return dbDomain; - } - - public String getDbName() { - return dbName; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public String getTableName() { - return tableName; - } - -} diff --git a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java index 3fcc46ba5653..4ccc0995dc18 100644 --- a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java @@ -13,18 +13,20 @@ public class ActiveRowTest { @Test void initialiseTest() { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow row = new ActiveRow(db, "1"); + ActiveDatabase activeDatabase = new ActiveDatabase("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(activeDatabase, "1"); assertDoesNotThrow(row::initialise); } @Test void readRowTest() { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow row = new ActiveRow(db, "1"); + ActiveDatabase activeDatabase = new ActiveDatabase("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(activeDatabase, "1"); + System.out.println(row.read().size()); + int rowSize = row.read().size(); if (!(row.read().equals(new ArrayList()))) { - for (int i = 0; i < row.read().size(); i++) { + for (int i = 0; i < rowSize; i++) { assertNotNull(row.read().get(i)); } } @@ -33,8 +35,8 @@ void readRowTest() { @Test void deleteRowTest() { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow row = new ActiveRow(db, "1"); + ActiveDatabase activeDatabase = new ActiveDatabase("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(activeDatabase, "1"); row.delete(); assertEquals(row.read(), new ArrayList()); @@ -42,8 +44,8 @@ void deleteRowTest() { @Test void insertRowTest() { - DB db = new DB("world", "root", "apple-trunks", "localhost:3306", "city"); - ActiveRow row = new ActiveRow(db, "1"); + ActiveDatabase activeDatabase = new ActiveDatabase("world", "root", "apple-trunks", "localhost:3306", "city"); + ActiveRow row = new ActiveRow(activeDatabase, "1"); row.delete(); row.contents = new ArrayList( Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); diff --git a/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java index 92d44b4e578b..61051204d5ec 100644 --- a/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java @@ -1,6 +1,5 @@ package com.iluwatar.activeobject; -import com.iluwatar.activeobject.App; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; From 3226b42be3056838307017a3b8286196a3c2c7cd Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 21:48:40 +1100 Subject: [PATCH 10/18] Update README.md --- active-record/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/active-record/README.md b/active-record/README.md index e69de29bb2d1..d0aef4712622 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -0,0 +1,15 @@ +--- +title: Active Record +category: Architectural +language: en +tags: + - Data access +--- + +## Intent +The active record design pattern is a way of accessing data from databases by creating an object which contains the contents of a database row. + +## Explanation +The ActiveRecord object is intitialised by an ActiveDatabase object which creates a connection to an exisitng database. + +Real-world example From f76b6727b45b99db88ef3a759fc04611a988df5a Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:24:00 +1100 Subject: [PATCH 11/18] Update README.md --- active-record/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/active-record/README.md b/active-record/README.md index d0aef4712622..c3e8f8fd4105 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -13,3 +13,19 @@ The active record design pattern is a way of accessing data from databases by cr The ActiveRecord object is intitialised by an ActiveDatabase object which creates a connection to an exisitng database. Real-world example +> You want to implement an application which access and update a database from within an OOP context. +> Update the database within a program. + +In plain words + +> For a system which requires database storage but do not have sufficient SQL knowledge to do so. + +Wikipedia says +>The active record pattern is an approach to accessing data in a database. A database table or view is wrapped into a class. Thus, an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database. When an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view. + + +**Programmatic Example** + +This implementation shows what the Active Record pattern could look like in a generic context. The Active Record makes initialises `contents` and `columns` as lists of strings. A developer can create an `ActiveDatabase` object by entering the configuration details required to start the connection. Assuming there is an exsiting database the developer would then create an `ActiveRecord` object using the `ActiveDatabase` object and the `id` of the row with which they would like to create in an active context. They are able to locate the row which matches this `id` then this can be read or deleted. + +If you are adding in a new row you can just update the `contents` list which would be empty if created with an `id` which doesnt exist yet. From 5bb02b54a527ef000b64753d99558907ada53457 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Sun, 30 Oct 2022 23:29:08 +1100 Subject: [PATCH 12/18] Finished, ready to pullrequest. --- active-record/pom.xml | 26 ++++++++++++++ .../iluwatar/activeobject/ActiveDatabase.java | 24 +++++++++++++ .../com/iluwatar/activeobject/ActiveRow.java | 34 ++++++++++++++----- .../java/com/iluwatar/activeobject/App.java | 24 +++++++++++++ .../com/iluwatar/activeobject/Rowverride.java | 24 +++++++++++++ .../iluwatar/activeobject/ActiveRowTest.java | 25 ++++++++++++++ .../com/iluwatar/activeobject/AppTest.java | 24 +++++++++++++ 7 files changed, 173 insertions(+), 8 deletions(-) diff --git a/active-record/pom.xml b/active-record/pom.xml index 9661de1449ab..150a69c98269 100644 --- a/active-record/pom.xml +++ b/active-record/pom.xml @@ -1,4 +1,30 @@ + 4.0.0 diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java index 0646d43dc98a..aafeebe8db56 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveDatabase.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import java.sql.SQLException; diff --git a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java index 24b048583a19..980a16e7e956 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/ActiveRow.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import java.sql.Connection; @@ -89,15 +113,9 @@ public void write() { query.append(" VALUES ("); for (String con : this.contents) { if (contents.indexOf(con) != this.contents.size() - 1) { - query.append("'"); - query.append(con); - query.append("' "); - query.append(","); + query.append("'").append(con).append("' ,"); } else { - query.append("'"); - query.append(con); - query.append("' "); - query.append(")"); + query.append("'").append(con).append("' )"); } } diff --git a/active-record/src/main/java/com/iluwatar/activeobject/App.java b/active-record/src/main/java/com/iluwatar/activeobject/App.java index 2266e25a1412..e4c31a7809b4 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/App.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/App.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import java.util.ArrayList; diff --git a/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java index 2088a308e4b5..79f6608aef1d 100644 --- a/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java +++ b/active-record/src/main/java/com/iluwatar/activeobject/Rowverride.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import java.lang.annotation.ElementType; diff --git a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java index 4ccc0995dc18..e8bba9d46648 100644 --- a/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/ActiveRowTest.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import java.util.ArrayList; @@ -49,6 +73,7 @@ void insertRowTest() { row.delete(); row.contents = new ArrayList( Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); + row.write(); assertEquals(row.read(), Arrays.asList("101", "Godoy Cruz", "ARG", "Mendoza", "206998")); } diff --git a/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java index 61051204d5ec..8fceeb5b32b5 100644 --- a/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java +++ b/active-record/src/test/java/com/iluwatar/activeobject/AppTest.java @@ -1,3 +1,27 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.activeobject; import org.junit.jupiter.api.Test; From fa06de0a0abce0b8308d41a9464ba8556bdfbb40 Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:35:45 +1100 Subject: [PATCH 13/18] Update README.md --- active-record/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-record/README.md b/active-record/README.md index c3e8f8fd4105..7b8a34430966 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -18,7 +18,7 @@ Real-world example In plain words -> For a system which requires database storage but do not have sufficient SQL knowledge to do so. +> For a system which requires database storage and ability to change its contents too. Wikipedia says >The active record pattern is an approach to accessing data in a database. A database table or view is wrapped into a class. Thus, an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database. When an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view. From 5478f39e984a08bc084213f5b08a4bb12057cca7 Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:48:44 +1100 Subject: [PATCH 14/18] Update README.md --- active-record/README.md | 144 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/active-record/README.md b/active-record/README.md index 7b8a34430966..0850a9f2e1a9 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -29,3 +29,147 @@ Wikipedia says This implementation shows what the Active Record pattern could look like in a generic context. The Active Record makes initialises `contents` and `columns` as lists of strings. A developer can create an `ActiveDatabase` object by entering the configuration details required to start the connection. Assuming there is an exsiting database the developer would then create an `ActiveRecord` object using the `ActiveDatabase` object and the `id` of the row with which they would like to create in an active context. They are able to locate the row which matches this `id` then this can be read or deleted. If you are adding in a new row you can just update the `contents` list which would be empty if created with an `id` which doesnt exist yet. + +```java +public class ActiveRow { + + String id; + ActiveDatabase dataBase; + Connection con; + String read; + String delete; + String write; + int columnCount; + ArrayList columns; + ArrayList contents = new ArrayList<>(); + + /** + * ActiveRow attempts to implement the fundamental ruby on rails 'Active Record' design pattern in + * Java. + * + * @param dataBase A Database object which handles opening the connection. + * @param id The unique identifier of a row. + */ + public ActiveRow(ActiveDatabase dataBase, String id) { + this.dataBase = dataBase; + this.id = id; + initialise(); + } + + /** + * This initialises the class by creating a connection and populating the column names and other + * variables which are used in the program. + */ + @Rowverride + public void initialise() { + try { + con = DriverManager + .getConnection("jdbc:mysql://" + dataBase.getDbDomain() + "/" + dataBase.getDbName(), + dataBase.getUsername(), dataBase.getPassword()); + } catch (SQLException e) { + e.printStackTrace(); + } + + try { + Statement statement = con.createStatement(); + ResultSet columnSet = statement.executeQuery( + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"); + ArrayList columnNames = new ArrayList(); + ResultSetMetaData rsmd = columnSet.getMetaData(); + columnCount = rsmd.getColumnCount(); + for (int i = 1; i < columnCount + 1; i++) { + String name = rsmd.getColumnName(i); + columnNames.add(name); + } + this.columns = columnNames; + read = + "SELECT * FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "` WHERE ID=" + + this.id; + delete = "DELETE FROM `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`" + + " WHERE ID = '" + this.id + "';"; + write = "INSERT INTO `" + dataBase.getDbName() + "`.`" + dataBase.getTableName() + "`"; + statement.close(); + columnSet.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + + } + + /** + * Writes contents of the active row object to the chosen database. + */ + @Rowverride + public void write() { + StringBuilder query = new StringBuilder(); + query.append(write); + query.append(" VALUES ("); + for (String con : this.contents) { + if (contents.indexOf(con) != this.contents.size() - 1) { + query.append("'").append(con).append("' ,"); + } else { + query.append("'").append(con).append("' )"); + } + } + + try { + PreparedStatement stmt = con.prepareStatement(query.toString()); + stmt.executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Deletes the current instance of the active row from the database based on the given ID value. + */ + @Rowverride + public void delete() { + if (this.read().equals(new ArrayList())) { + System.out.println("Row does not exist."); + return; + } + try { + con.prepareStatement(delete).executeUpdate(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Reads the current active row. + * + * @return the current active row contents as Arraylist + */ + @Rowverride + public ArrayList read() { + try { + Statement statement = con.createStatement(); + + ResultSet rowResult = statement.executeQuery(read); + while (rowResult.next()) { + for (int col = 1; col <= columnCount; col++) { + Object value = rowResult.getObject(col); + if (value != null) { + contents.add(value.toString()); + } + } + } + rowResult.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + + return this.contents; + } + +} +``` + + +![alt text](./etc/active-record.png "Active Record Traits and Domain") From 7287c04d7e1ffee42849f35626ce288ce01b5c80 Mon Sep 17 00:00:00 2001 From: Andrew Carse Date: Sun, 30 Oct 2022 23:48:56 +1100 Subject: [PATCH 15/18] Finished, ready to pullrequest. --- active-record/etc/active-record.png | Bin 0 -> 58364 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 active-record/etc/active-record.png diff --git a/active-record/etc/active-record.png b/active-record/etc/active-record.png new file mode 100644 index 0000000000000000000000000000000000000000..976318ed532380604164ef524eb3229e92fb62cd GIT binary patch literal 58364 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 &C}|`;}ob0l^E5g>;9#4U>@$ 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 z Date: Sun, 30 Oct 2022 23:49:51 +1100 Subject: [PATCH 16/18] Update README.md --- active-record/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/active-record/README.md b/active-record/README.md index 0850a9f2e1a9..6ddc969b2d41 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -172,4 +172,4 @@ public class ActiveRow { ``` -![alt text](./etc/active-record.png "Active Record Traits and Domain") +![alt text](./etc/active-record.png) From 9b23adee0e242b5ebfe840e6bef335ba4fc0276c Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:54:18 +1100 Subject: [PATCH 17/18] Update README.md --- active-record/README.md | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/active-record/README.md b/active-record/README.md index 6ddc969b2d41..f73748d8a0c5 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -171,5 +171,57 @@ public class ActiveRow { } ``` +The ActiveDatabase class is used as an object which contains all necessary information pertaining to setting up the database connection. +```java +public class ActiveDatabase { + + final String dbDomain; + final String dbName; + final String username; + final String password; + final String tableName; + + /** + * Constructor needed to instantiate the database object which is used with the MySQLWorkbench + * Database. + * + * @param dbName Name of the database as String. + * @param username Username for the database as String. + * @param password Password for the database as String. + * @param dbDomain The domain for the database as String. Tested on localhost:3306 + * @param tableName Name of the table which you wish to create the active row. + */ + public ActiveDatabase(String dbName, String username, String password, String dbDomain, + String tableName) { + this.dbDomain = dbDomain; + this.dbName = dbName; + this.username = username; + this.password = password; + this.tableName = tableName; + } + + public String getDbDomain() { + return dbDomain; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTableName() { + return tableName; + } + +} + +``` ![alt text](./etc/active-record.png) From 1bd6cc1c0c3d2facb1379e9ccb11463ceadbfccc Mon Sep 17 00:00:00 2001 From: Andrew Carse <59854659+TwentyVentti@users.noreply.github.com> Date: Sun, 30 Oct 2022 23:58:49 +1100 Subject: [PATCH 18/18] Update README.md --- active-record/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/active-record/README.md b/active-record/README.md index f73748d8a0c5..d6114eea7011 100644 --- a/active-record/README.md +++ b/active-record/README.md @@ -224,4 +224,7 @@ public class ActiveDatabase { ``` +## Applicability +Useful for when you require objects which are made up of database rows and may need to manipulate the database during your program. + ![alt text](./etc/active-record.png)