diff --git a/projects/liza22/pom.xml b/projects/liza22/pom.xml new file mode 100644 index 0000000..2c6271b --- /dev/null +++ b/projects/liza22/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + ru.mipt.diht.students + parent + 1.0-SNAPSHOT + + ru.mipt.diht.students + liza22 + 1.0-SNAPSHOT + liza22 + http://maven.apache.org + + UTF-8 + 1.7 + 1.7 + + + + + com.h2database + h2 + 1.4.190 + + + org.powermock + powermock-module-junit4 + 1.6.1 + test + true + + + junit + junit + + + + + + com.beust + jcommander + 1.48 + + diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/App.java b/projects/liza22/src/main/java/ru/mipt/diht/students/App.java new file mode 100644 index 0000000..31d6fa9 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/App.java @@ -0,0 +1,11 @@ +package ru.mipt.diht.students; + +/** + * Hello world! + * + */ +public class App { + public static void main(String[] args) { + System.out.println("Hello World!"); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/pom.xml b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/pom.xml new file mode 100644 index 0000000..877c46d --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + mini.orm + orm + 1.0 + + + 1.7 + 1.7 + + + + + com.h2database + h2 + 1.4.190 + + + junit + junit + 4.4 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.16 + + src/checkstyle.xml + src/checkstyle-suppressions.xml + Cp1251 + true + true + + + + validate + validate + + check + + + + + + com.puppycrawl.tools + checkstyle + 6.10.1 + + + + + + + \ No newline at end of file diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle-suppressions.xml b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle-suppressions.xml new file mode 100644 index 0000000..ecd3542 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle-suppressions.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle.xml b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle.xml new file mode 100644 index 0000000..3401cf5 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/checkstyle.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Column.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Column.java new file mode 100644 index 0000000..f82e03d --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Column.java @@ -0,0 +1,16 @@ +package mini.orm.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/* + * Annotation for Table Columns. + * Default value is used now "", but it will be used as "field_name" later in code. + */ +@Target(value = ElementType.FIELD) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface Column { + String name() default ""; +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/DatabaseService.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/DatabaseService.java new file mode 100644 index 0000000..413ae39 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/DatabaseService.java @@ -0,0 +1,25 @@ +package mini.orm.api; + +import java.util.List; + +/** + * Main ORM interface. + * @param type of entity + * @param type of primary key for entity + */ +public interface DatabaseService { + + T queryById(K entityKey); + + List queryForAll(); + + void insert(T newEntity); + + void update(T updatedEntity); + + void delete(T deleteEntity); + + void createTable(); + + void dropTable(); +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/PrimaryKey.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/PrimaryKey.java new file mode 100644 index 0000000..4a462fd --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/PrimaryKey.java @@ -0,0 +1,14 @@ +package mini.orm.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for Column which is Primary Key of table. + */ +@Target(value = ElementType.FIELD) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface PrimaryKey { +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Table.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Table.java new file mode 100644 index 0000000..c4ad8c1 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/api/Table.java @@ -0,0 +1,16 @@ +package mini.orm.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for Table. + * Default value is used now "", but it will be used as "class_name" later in code. + */ +@Target(value = ElementType.TYPE) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface Table { + String name() default ""; +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/JdbcDatabaseService.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/JdbcDatabaseService.java new file mode 100644 index 0000000..75832a9 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/JdbcDatabaseService.java @@ -0,0 +1,182 @@ +package mini.orm.core; + +import mini.orm.api.DatabaseService; +import mini.orm.core.db.ConnectionProvider; +import mini.orm.core.db.impl.JdbcConnectionProvider; +import mini.orm.core.model.ColumnDescriptor; +import mini.orm.core.model.TableDescriptor; +import mini.orm.core.utils.Constants; +import mini.orm.core.utils.QueryStorage; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +/** + * implementation ORM-service by JDBC base. + * @param - content type + * @param - primary key type + */ +public final class JdbcDatabaseService implements DatabaseService { + private static final boolean LOGGING = true; + + private Class entityType; + private TableDescriptor tableDescriptor; + + private ConnectionProvider connectionProvider; + + public JdbcDatabaseService(Class entityClass) { + this.entityType = entityClass; + this.tableDescriptor = TableDescriptor.fromEntityClass(entityClass); + this.connectionProvider = new JdbcConnectionProvider(); + } + + public T queryById(K entityKey) { + // get query with TABLE name and primary key name + String query = QueryStorage.getSelectByPkQuery(tableDescriptor.getTableName(), + tableDescriptor.getPrimaryKey().getColumnName()); + if (LOGGING) { + System.out.println("Method 'queryById' query is: " + query); + } + // make DB connection, then PreparedStatement, run query and processing the ResultSet + try (Connection conn = connectionProvider.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setObject(1, entityKey); + try (ResultSet result = stmt.executeQuery()) { + if (result.next()) { + return buildEntityFromResult(result); + } else { + return null; + } + } + } catch (SQLException e) { + System.err.println("Error while perform queryById: " + e.getMessage()); + return null; + } + } + + public List queryForAll() { + String query = QueryStorage.getSelectAllQuery(tableDescriptor.getTableName()); + if (LOGGING) { + System.out.println("Method 'queryForAll' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); + Statement stmt = conn.createStatement(); + ResultSet result = stmt.executeQuery(query)) { + List entities = new ArrayList<>(); + while (result.next()) { + entities.add(buildEntityFromResult(result)); + } + return entities; + } catch (SQLException e) { + System.err.println("Error while perform queryForAll: " + e.getMessage()); + return null; + } + } + + public void insert(T newEntity) { + String query = QueryStorage.getInsertQuery(tableDescriptor.getTableName(), tableDescriptor.getColumns().size()); + if (LOGGING) { + System.out.println("Method 'insert' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + for (ColumnDescriptor columnDescriptor : tableDescriptor.getColumns()) { + int bindNum = tableDescriptor.getColumns().indexOf(columnDescriptor) + 1; + Object columnValue = columnDescriptor.getColumnField().get(newEntity); + stmt.setObject(bindNum, columnValue); + } + stmt.executeUpdate(); + } catch (SQLException | IllegalAccessException e) { + System.err.println("Error while perform insert: " + e.getMessage()); + } + } + + public void update(T updatedEntity) { + List updateColumns = tableDescriptor.getColumnsExceptPK(); + String[] updateColumnNames = new String[updateColumns.size()]; + for (ColumnDescriptor cd : updateColumns) { + updateColumnNames[updateColumns.indexOf(cd)] = cd.getColumnName(); + } + String query = QueryStorage.getUpdateByPkQuery(tableDescriptor.getTableName(), + updateColumnNames, tableDescriptor.getPrimaryKey().getColumnName()); + if (LOGGING) { + System.out.println("Method 'update' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + int bindNum = 0; + for (ColumnDescriptor columnDescriptor : updateColumns) { + bindNum = updateColumns.indexOf(columnDescriptor) + 1; + Object columnValue = columnDescriptor.getColumnField().get(updatedEntity); + stmt.setObject(bindNum, columnValue); + } + stmt.setObject(bindNum + 1, tableDescriptor.getPrimaryKey().getColumnField().get(updatedEntity)); + stmt.executeUpdate(); + } catch (SQLException | IllegalAccessException e) { + System.err.println("Error while perform update: " + e.getMessage()); + } + } + + public void delete(T deleteEntity) { + ColumnDescriptor pkColumn = tableDescriptor.getPrimaryKey(); + String query = QueryStorage.getDeleteByPkQuery(tableDescriptor.getTableName(), pkColumn.getColumnName()); + if (LOGGING) { + System.out.println("Method 'delete' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); + PreparedStatement stmt = conn.prepareStatement(query)) { + stmt.setObject(1, pkColumn.getColumnField().get(deleteEntity)); + stmt.executeUpdate(); + } catch (SQLException | IllegalAccessException e) { + System.err.println("Error while perform delete: " + e.getMessage()); + } + } + + public void createTable() { + List columns = tableDescriptor.getColumns(); + String[] columnNames = new String[columns.size()]; + String[] columnTypes = new String[columns.size()]; + for (int i = 0; i < columns.size(); i++) { + ColumnDescriptor columnDescriptor = columns.get(i); + columnNames[i] = columnDescriptor.getColumnName(); + columnTypes[i] = Constants.JAVA_TO_SQL_TYPES.get(columnDescriptor.getColumnType()); + } + String query = QueryStorage.getCreateTableQuery(tableDescriptor.getTableName(), + columnNames, columnTypes, tableDescriptor.getPrimaryKey().getColumnName()); + if (LOGGING) { + System.out.println("Method 'createTable' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); Statement stmt = conn.createStatement()) { + stmt.executeUpdate(query); + } catch (SQLException e) { + System.err.println("Error while perform createTable: " + e.getMessage()); + } + } + + public void dropTable() { + String query = QueryStorage.getDropTableQuery(tableDescriptor.getTableName()); + if (LOGGING) { + System.out.println("Method 'dropTable' query is: " + query); + } + try (Connection conn = connectionProvider.getConnection(); Statement stmt = conn.createStatement()) { + stmt.executeUpdate(query); + } catch (SQLException e) { + System.err.println("Error while perform dropTable: " + e.getMessage()); + } + } + + private T buildEntityFromResult(ResultSet resultSet) { + try { + T entity = entityType.newInstance(); + for (ColumnDescriptor column : tableDescriptor.getColumns()) { + Object columnValue = resultSet.getObject(column.getColumnName()); + column.getColumnField().set(entity, columnValue); + } + return entity; + } catch (Exception e) { + System.err.println("Entity class = " + entityType + " is incorrect: " + e.getMessage()); + return null; + } + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/ConnectionProvider.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/ConnectionProvider.java new file mode 100644 index 0000000..b07e365 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/ConnectionProvider.java @@ -0,0 +1,12 @@ +package mini.orm.core.db; + +import java.sql.Connection; +import java.sql.SQLException; + +/* + * provide Connection to DB. + */ +public interface ConnectionProvider { + + Connection getConnection() throws SQLException; +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/DbConfig.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/DbConfig.java new file mode 100644 index 0000000..2817a4b --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/DbConfig.java @@ -0,0 +1,107 @@ +package mini.orm.core.db; + +import mini.orm.core.utils.Constants; + +import java.io.*; + +/* + * config file with parameters DB connection's. + * will be use by DriverManager to getting DB Connection. + */ +public class DbConfig { + /* + * keys names from property file. + */ + private static final String JDBC_DRIVER_PROP_NAME = "db.jdbc.driver"; + private static final String DB_URL_PROP_NAME = "db.url"; + private static final String DB_USER_PROP_NAME = "db.user"; + private static final String DB_PASSWORD_PROP_NAME = "db.password"; + + private String jdbcDriver; + private String url; + private String username; + private String password; + + public DbConfig() { + init(); + // load Jdbc Driver class + try { + Class.forName(jdbcDriver); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Driver Class = '" + jdbcDriver + "' not found", e); + } + } + + private void init() { + File cfgFile = new File(Constants.DB_CONFIG_FILE); + try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(cfgFile)))) { + while (in.ready()) { + String line = in.readLine(); + int indexOfDelimiter = line.indexOf('='); + if (indexOfDelimiter == -1) { + System.err.println("Incorrect line in DB configuration = '" + line + "'"); + continue; + } + String propName = line.substring(0, indexOfDelimiter); + String propValue = line.substring(indexOfDelimiter + 1, line.length()); + switch (propName) { + case JDBC_DRIVER_PROP_NAME: + jdbcDriver = propValue; + break; + case DB_URL_PROP_NAME: + url = propValue; + break; + case DB_USER_PROP_NAME: + username = propValue; + break; + case DB_PASSWORD_PROP_NAME: + password = propValue; + break; + default: + System.err.println("Property '" + propName + "' not recognized"); + } + } + } catch (FileNotFoundException e) { + System.err.println("DB config file by path = \"" + cfgFile.getAbsolutePath() + "\" not found"); + } catch (IOException e) { + System.err.println("Problem with reading DB config file: " + e.getMessage()); + } + + validate(); + } + + private void validate() { + if (jdbcDriver == null + || url == null + || username == null + || password == null) { + throw new IllegalStateException("DB configuration file is incorrect"); + } + } + + public final String getJdbcDriver() { + return jdbcDriver; + } + + public final String getUrl() { + return url; + } + + public final String getUsername() { + return username; + } + + public final String getPassword() { + return password; + } + + @Override + public final String toString() { + return "DbConfig{" + + "jdbcDriver='" + jdbcDriver + '\'' + + ", url='" + url + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + '}'; + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/impl/JdbcConnectionProvider.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/impl/JdbcConnectionProvider.java new file mode 100644 index 0000000..0b1fb6b --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/db/impl/JdbcConnectionProvider.java @@ -0,0 +1,24 @@ +package mini.orm.core.db.impl; + +import mini.orm.core.db.ConnectionProvider; +import mini.orm.core.db.DbConfig; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * ConnectionProvider on the JDBC base. + * Using DB config with parameters to make Connection. + */ +public class JdbcConnectionProvider implements ConnectionProvider { + private DbConfig dbConfig; + + public JdbcConnectionProvider() { + this.dbConfig = new DbConfig(); + } + + public final Connection getConnection() throws SQLException { + return DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword()); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/ColumnDescriptor.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/ColumnDescriptor.java new file mode 100644 index 0000000..5fc8d36 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/ColumnDescriptor.java @@ -0,0 +1,48 @@ +package mini.orm.core.model; + +import mini.orm.api.Column; +import mini.orm.core.utils.NameUtils; + +import java.lang.reflect.Field; + +/** + * this class describes one column of table. + */ +public class ColumnDescriptor { + private String name; + private Class type; + private Field columnField; + + ColumnDescriptor(String columnName, Class columnType, Field columnField1) { + this.name = columnName; + this.type = columnType; + this.columnField = columnField1; + columnField.setAccessible(true); + } + + public final String getColumnName() { + return name; + } + + public final Class getColumnType() { + return type; + } + + public final Field getColumnField() { + return columnField; + } + + public static ColumnDescriptor fromEntityField(Field entityField) { + if (!entityField.isAnnotationPresent(Column.class)) { + throw new IllegalArgumentException("Field = " + entityField + " not annotated by @Column"); + } + + String columnName = entityField.getAnnotation(Column.class).name(); + if (columnName.isEmpty()) { + // use default value - field name + columnName = NameUtils.convertFromCamelCaseToUnderscore(entityField.getName()); + } + Class columnType = entityField.getType(); + return new ColumnDescriptor(columnName, columnType, entityField); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/TableDescriptor.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/TableDescriptor.java new file mode 100644 index 0000000..701034c --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/model/TableDescriptor.java @@ -0,0 +1,79 @@ +package mini.orm.core.model; + +import mini.orm.api.PrimaryKey; +import mini.orm.api.Table; +import mini.orm.core.utils.NameUtils; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/* + * create descriptor to Table + */ +public final class TableDescriptor { + private String name; + private ColumnDescriptor primaryKey; + private List columns; + + TableDescriptor(String tableName) { + this.name = tableName; + this.columns = new ArrayList<>(); + } + + void addColumn(ColumnDescriptor columnDescriptor) { + columns.add(columnDescriptor); + } + + void setPrimaryKey(ColumnDescriptor primaryKeyColumn) { + this.primaryKey = primaryKeyColumn; + } + + public String getTableName() { + return name; + } + + public List getColumns() { + return Collections.unmodifiableList(columns); + } + + public List getColumnsExceptPK() { + List columnsExceptPK = new ArrayList<>(columns); + columnsExceptPK.remove(primaryKey); + return columnsExceptPK; + } + + public ColumnDescriptor getPrimaryKey() { + return primaryKey; + } + + public static TableDescriptor fromEntityClass(Class entityClass) { + if (!entityClass.isAnnotationPresent(Table.class)) { + throw new IllegalArgumentException("Class = " + entityClass.getSimpleName() + + " is not annotated by @Table"); + } + String tableName = entityClass.getAnnotation(Table.class).name(); + if (tableName.isEmpty()) { + // use default - class name + tableName = NameUtils.convertFromCamelCaseToUnderscore(entityClass.getSimpleName()); + } + + TableDescriptor tableDescriptor = new TableDescriptor(tableName); + boolean primaryKeyExists = false; + for (Field entityField : entityClass.getDeclaredFields()) { + ColumnDescriptor columnDescriptor = ColumnDescriptor.fromEntityField(entityField); + tableDescriptor.addColumn(columnDescriptor); + if (entityField.isAnnotationPresent(PrimaryKey.class)) { + tableDescriptor.setPrimaryKey(columnDescriptor); + primaryKeyExists = true; + } + } + if (!primaryKeyExists) { + throw new IllegalArgumentException("Entity class = " + entityClass.getSimpleName() + + " does not have @PrimaryKey field"); + } + + return tableDescriptor; + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/Constants.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/Constants.java new file mode 100644 index 0000000..07f9aca --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/Constants.java @@ -0,0 +1,28 @@ +package mini.orm.core.utils; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Project constants. + */ +public class Constants { + public static final String DB_CONFIG_FILE = "db.config"; + + /** + * correlate Java-types with SQL-types. + */ + public static final Map, String> JAVA_TO_SQL_TYPES; + static { + JAVA_TO_SQL_TYPES = new HashMap<>(); + JAVA_TO_SQL_TYPES.put(Boolean.class, "BOOLEAN"); + JAVA_TO_SQL_TYPES.put(boolean.class, "BOOLEAN"); + JAVA_TO_SQL_TYPES.put(Integer.class, "INTEGER"); + JAVA_TO_SQL_TYPES.put(int.class, "INTEGER"); + JAVA_TO_SQL_TYPES.put(Long.class, "BIGINT"); + JAVA_TO_SQL_TYPES.put(long.class, "BIGINT"); + JAVA_TO_SQL_TYPES.put(String.class, "VARCHAR(256)"); + JAVA_TO_SQL_TYPES.put(Date.class, "TIMESTAMP"); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/NameUtils.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/NameUtils.java new file mode 100644 index 0000000..6975465 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/NameUtils.java @@ -0,0 +1,13 @@ +package mini.orm.core.utils; + +public final class NameUtils { + + /* + * convert CamelCase format string to underscore format using regexp + */ + public static String convertFromCamelCaseToUnderscore(String ccStr) { + String regex = "([a-z])([A-Z])"; + String replacement = "$1_$2"; + return ccStr.replaceAll(regex, replacement).toLowerCase(); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/QueryStorage.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/QueryStorage.java new file mode 100644 index 0000000..ced0024 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/main/java/mini/orm/core/utils/QueryStorage.java @@ -0,0 +1,71 @@ +package mini.orm.core.utils; + +import java.text.MessageFormat; + +/* + * this class contains SQL queries teplates. + */ +public class QueryStorage { + + private static final String SELECT_BY_PK_QUERY = + "SELECT * FROM {0} WHERE {1} = ?"; + public static String getSelectByPkQuery(String tableName, String pkColumnName) { + return MessageFormat.format(SELECT_BY_PK_QUERY, tableName, pkColumnName); + } + + private static final String SELECT_ALL_QUERY = + "SELECT * FROM {0}"; + public static String getSelectAllQuery(String tableName) { + return MessageFormat.format(SELECT_ALL_QUERY, tableName); + } + + private static final String INSERT_QUERY = + "INSERT INTO {0} VALUES({1})"; + public static String getInsertQuery(String tableName, int countOfColumns) { + StringBuilder valueBinds = new StringBuilder(); + for (int i = 0; i < countOfColumns; i++) { + valueBinds.append("?"); + if (i != countOfColumns - 1) { + valueBinds.append(", "); + } + } + return MessageFormat.format(INSERT_QUERY, tableName, valueBinds); + } + + private static final String UPDATE_BY_PK_QUERY = + "UPDATE {0} SET {1} WHERE {2} = ?"; + public static String getUpdateByPkQuery(String tableName, String[] columns, String pkColumnName) { + StringBuilder valueBinds = new StringBuilder(); + for (int i = 0; i < columns.length; i++) { + valueBinds.append(columns[i]).append("=?"); + if (i != columns.length - 1) { + valueBinds.append(", "); + } + } + return MessageFormat.format(UPDATE_BY_PK_QUERY, tableName, valueBinds, pkColumnName); + } + + private static final String DELETE_BY_PK_QUERY = + "DELETE FROM {0} WHERE {1} = ?"; + public static String getDeleteByPkQuery(String tableName, String pkColumnName) { + return MessageFormat.format(DELETE_BY_PK_QUERY, tableName, pkColumnName); + } + + private static final String CREATE_TABLE_QUERY = "CREATE TABLE {0} ({1} PRIMARY KEY ( {2} ))"; + public static String getCreateTableQuery(String tableName, String[] columnNames, + String[] columnTypes, String pkColumnName) { + StringBuilder columnsInfo = new StringBuilder(); + for (int i = 0; i < columnNames.length; i++) { + String columnName = columnNames[i]; + String columnType = columnTypes[i]; + columnsInfo.append(columnName).append(" ").append(columnType).append(", "); + } + return MessageFormat.format(CREATE_TABLE_QUERY, tableName, columnsInfo, pkColumnName); + } + + private static final String DROP_TABLE_QUERY = + "DROP TABLE {0}"; + public static String getDropTableQuery(String tableName) { + return MessageFormat.format(DROP_TABLE_QUERY, tableName); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/DatabaseServiceModuleTest.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/DatabaseServiceModuleTest.java new file mode 100644 index 0000000..d9c6d68 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/DatabaseServiceModuleTest.java @@ -0,0 +1,136 @@ +package mini.orm; + +import mini.orm.api.DatabaseService; +import mini.orm.model.CarEntity; +import mini.orm.model.IncorrectEntityWithoutColumnAnn; +import mini.orm.model.IncorrectEntityWithoutPrimaryKeyAnn; +import mini.orm.model.IncorrectEntityWithoutTableAnn; +import mini.orm.core.JdbcDatabaseService; +import org.h2.tools.Server; +import org.junit.Test; +import org.junit.Before; +import org.junit.After; + +import java.sql.SQLException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.List; + +import static org.junit.Assert.*; + +public final class DatabaseServiceModuleTest { + private static final int N3 = 3; + private static final int N5 = 5; + private static final int N10 = 10; + + private DatabaseService carDbService = new JdbcDatabaseService<>(CarEntity.class); + + @Before + public void setUp() throws SQLException { + // start DB server + Server.createTcpServer("-tcpPort", "9001", "-tcpAllowOthers").start(); + // firstly create table + carDbService.createTable(); + } + + @After + public void setDown() throws SQLException { + // drop table if it exists + carDbService.dropTable(); + // stop DB server + Server.shutdownTcpServer("tcp://localhost:9001", "", true, true); + } + + @Test + public void restAllDmlOperations() throws ParseException { + // add data to table + final CarEntity car1 = new CarEntity(); + car1.setId(1); + car1.setName("Car#1"); + car1.setColor("green"); + car1.setTruck(false); + car1.setCountOfDoors(N5); + car1.setReleaseDate(new SimpleDateFormat("YYYY-MM-DD").parse("2010-12-01")); + + final CarEntity car2 = new CarEntity(); + car2.setId(2); + car2.setName("Car#2"); + car2.setColor("blue"); + car2.setTruck(false); + car2.setCountOfDoors(N3); + car2.setReleaseDate(new SimpleDateFormat("YYYY-MM-DD").parse("2013-01-20")); + + final CarEntity truck = new CarEntity(); + truck.setId(N3); + truck.setName("MegaTruck"); + truck.setColor("grey"); + truck.setTruck(true); + truck.setCountOfDoors(2); + truck.setReleaseDate(new SimpleDateFormat("YYYY-MM-DD").parse("2000-07-15")); + + carDbService.insert(car1); + carDbService.insert(car2); + carDbService.insert(truck); + + // select entity by primary key (car_2 expected) + CarEntity carByPK = carDbService.queryById(2); + assertNotNull(carByPK); + assertEquals(car2, carByPK); + + CarEntity notExistedCar = carDbService.queryById(N10); + assertNull(notExistedCar); + + // select all cars + List cars = carDbService.queryForAll(); + assertEquals(N3, cars.size()); + assertTrue(cars.contains(car1)); + assertTrue(cars.contains(car2)); + assertTrue(cars.contains(truck)); + + // update any car + CarEntity newTruck = new CarEntity(); + newTruck.setId(N3); + newTruck.setName("New TheMegaTruck"); + newTruck.setColor("dark-grey"); + newTruck.setTruck(true); + newTruck.setCountOfDoors(2); + newTruck.setReleaseDate(new SimpleDateFormat("YYYY-MM-DD").parse("2015-07-15")); + carDbService.update(newTruck); + // select new truck and validate that it updated + CarEntity newTruckSelected = carDbService.queryById(N3); + assertEquals(newTruck, newTruckSelected); + + // delete cars + carDbService.delete(car1); + carDbService.delete(car2); + // select all cars and check that deletion completed + List carsAfterDeletion = carDbService.queryForAll(); + assertEquals(1, carsAfterDeletion.size()); + assertTrue(carsAfterDeletion.contains(newTruck)); + + carDbService.delete(newTruck); + List noCarsAnymore = carDbService.queryForAll(); + assertTrue(noCarsAnymore.isEmpty()); + } + + @Test(expected = IllegalArgumentException.class) + public void testIncorrectEntityWithoutTableAnnotation() { + DatabaseService dbService = + new JdbcDatabaseService<>(IncorrectEntityWithoutTableAnn.class); + dbService.queryForAll(); + } + + @Test(expected = IllegalArgumentException.class) + public void testIncorrectEntityWithoutColumnAnnotation() { + DatabaseService dbService = + new JdbcDatabaseService<>(IncorrectEntityWithoutColumnAnn.class); + dbService.queryForAll(); + } + + @Test(expected = IllegalArgumentException.class) + public void testIncorrectEntityWithoutPrimaryKeyAnnotation() { + DatabaseService dbService = + new JdbcDatabaseService<>(IncorrectEntityWithoutPrimaryKeyAnn.class); + dbService.queryForAll(); + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/CarEntity.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/CarEntity.java new file mode 100644 index 0000000..1db881c --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/CarEntity.java @@ -0,0 +1,149 @@ +package mini.orm.model; + +import mini.orm.api.Column; +import mini.orm.api.PrimaryKey; +import mini.orm.api.Table; + +import java.util.Date; + +@Table(name = "cars") +public final class CarEntity { + + private static final int CONST31 = 31; + + @PrimaryKey + @Column(name = "car_id") + private int id; + + @Column + private String name; + + @Column + private String color; + + @Column + private int countOfDoors; + + @Column(name = "is_truck") + private boolean truck; + + @Column + private Date releaseDate; + + public int getId() { + return id; + } + + public void setId(int carid) { + this.id = carid; + } + + public String getName() { + return name; + } + + public void setName(String carname) { + this.name = carname; + } + + public String getColor() { + return color; + } + + public void setColor(String carcolor) { + this.color = carcolor; + } + + public int getCountOfDoors() { + return countOfDoors; + } + + public void setCountOfDoors(int carcountOfDoors) { + this.countOfDoors = carcountOfDoors; + } + + public boolean isTruck() { + return truck; + } + + public void setTruck(boolean cartruck) { + this.truck = cartruck; + } + + public Date getReleaseDate() { + return releaseDate; + } + + public void setReleaseDate(Date carreleaseDate) { + this.releaseDate = carreleaseDate; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + CarEntity carEntity = (CarEntity) o; + + if (id != carEntity.id) { + return false; + } + if (countOfDoors != carEntity.countOfDoors) { + return false; + } + if (truck != carEntity.truck) { + return false; + } + if (name != null && !name.equals(carEntity.name) || name == null && carEntity.name != null) { + return false; + } + if (color != null && !color.equals(carEntity.color) + || color == null && carEntity.color != null) { + return false; + } + return !(releaseDate != null && !releaseDate.equals(carEntity.releaseDate) + || releaseDate == null && carEntity.releaseDate != null); + + } + + @Override + public int hashCode() { + int result = id; + result = CONST31 * result; + if (name != null) { + result = result + name.hashCode(); + } + result = CONST31 * result; + if (color != null) { + result = result + color.hashCode(); + } + result = CONST31 * result + countOfDoors; + result = CONST31 * result; + if (truck) { + result = result + 1; + } + result = CONST31 * result; + if (releaseDate != null) { + result = result + releaseDate.hashCode(); + } + return result; + } + + @Override + public String toString() { + return "CarEntity{" + + "id=" + id + + ", name='" + name + + '\'' + + ", color='" + color + + '\'' + + ", countOfDoors=" + countOfDoors + + ", truck=" + truck + + ", releaseDate=" + releaseDate + + '}'; + } +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutColumnAnn.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutColumnAnn.java new file mode 100644 index 0000000..ad670d0 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutColumnAnn.java @@ -0,0 +1,15 @@ +package mini.orm.model; + +import mini.orm.api.Column; +import mini.orm.api.PrimaryKey; +import mini.orm.api.Table; + +@Table +public class IncorrectEntityWithoutColumnAnn { + + @PrimaryKey + @Column + private Integer id; + + private String name; +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutPrimaryKeyAnn.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutPrimaryKeyAnn.java new file mode 100644 index 0000000..b836ce9 --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutPrimaryKeyAnn.java @@ -0,0 +1,14 @@ +package mini.orm.model; + +import mini.orm.api.Column; +import mini.orm.api.Table; + +@Table +public class IncorrectEntityWithoutPrimaryKeyAnn { + + @Column + private Integer id; + + @Column + private String name; +} diff --git a/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutTableAnn.java b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutTableAnn.java new file mode 100644 index 0000000..3af530e --- /dev/null +++ b/projects/liza22/src/main/java/ru/mipt/diht/students/liza22/MiniORM/src/test/java/mini/orm/model/IncorrectEntityWithoutTableAnn.java @@ -0,0 +1,14 @@ +package mini.orm.model; + +import mini.orm.api.Column; +import mini.orm.api.PrimaryKey; + +public class IncorrectEntityWithoutTableAnn { + + @PrimaryKey + @Column + private Integer id; + + @Column + private String name; +} diff --git a/projects/liza22/src/test/java/ru/mipt/diht/students/AppTest.java b/projects/liza22/src/test/java/ru/mipt/diht/students/AppTest.java new file mode 100644 index 0000000..4bb8dd4 --- /dev/null +++ b/projects/liza22/src/test/java/ru/mipt/diht/students/AppTest.java @@ -0,0 +1,38 @@ +package ru.mipt.diht.students; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/projects/pom.xml b/projects/pom.xml index 5d14966..5fc3c22 100644 --- a/projects/pom.xml +++ b/projects/pom.xml @@ -1,5 +1,5 @@ - + + 4.0.0 ru.mipt.diht.students @@ -30,7 +30,8 @@ dkhurtin ale3otik Pitovsky - + liza22 + @@ -133,4 +134,4 @@ - + \ No newline at end of file