diff --git a/.gitignore b/.gitignore index 1e056e9..8797c1c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ workbench.xmi *.swp .settings .checkstyle +twitter4j.properties +key.txt \ No newline at end of file diff --git a/projects/andreyzharkov/java b/projects/andreyzharkov/java new file mode 100644 index 0000000..e69de29 diff --git a/projects/andreyzharkov/pom.xml b/projects/andreyzharkov/pom.xml new file mode 100644 index 0000000..48c16af --- /dev/null +++ b/projects/andreyzharkov/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + ru.mipt.diht.students + andreyzharkov + 1.0-SNAPSHOT + jar + + andreyzharkov + http://maven.apache.org + + + Travis CI + https://travis-ci.org/KhurtinDN/fizteh-java-2015 + + + + UTF-8 + + + + ru.mipt.diht.students + parent + 1.0-SNAPSHOT + + + + + junit + junit + 4.12 + test + + + com.h2database + h2 + 1.4.190 + + + com.google.guava + guava + 19.0 + + + org.twitter4j + twitter4j-core + [4.0,) + + + org.twitter4j + twitter4j-stream + [4.0,) + + + com.beust + jcommander + 1.48 + + + + + + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + maven-assembly-plugin + + + jar-with-dependencies + + + + ru.mipt.diht.students.andreyzharkov.miniORM.DatabaseService + + + + + + package + + single + + + + + + + + + diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/AnnotatedField.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/AnnotatedField.java new file mode 100644 index 0000000..18b9a2f --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/AnnotatedField.java @@ -0,0 +1,63 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM; + +import com.google.common.base.CaseFormat; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.Column; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Андрей on 17.12.2015. + */ +@SuppressWarnings("Duplicates") +public class AnnotatedField { + private String columnName; + private Field field; + + private static final Map SQL_TYPE; + + static { + SQL_TYPE = new HashMap<>(); + SQL_TYPE.put(Integer.class, "INT"); + SQL_TYPE.put(Long.class, "INT"); + SQL_TYPE.put(Byte.class, "INT"); + SQL_TYPE.put(Short.class, "INT"); + SQL_TYPE.put(Double.class, "DOUBLE"); + SQL_TYPE.put(Float.class, "DOUBLE"); + SQL_TYPE.put(String.class, "VARCHAR(10)"); + SQL_TYPE.put(Character.class, "VARCHAR(10)"); + SQL_TYPE.put(Integer.TYPE, "INT"); + SQL_TYPE.put(Long.TYPE, "INT"); + SQL_TYPE.put(Byte.TYPE, "INT"); + SQL_TYPE.put(Short.TYPE, "INT"); + SQL_TYPE.put(Double.TYPE, "DOUBLE"); + SQL_TYPE.put(Float.TYPE, "DOUBLE"); + SQL_TYPE.put(Character.TYPE, "VARCHAR(10)"); + } + + public final String getColumnName() { + return columnName; + } + + public final Field getField() { + return field; + } + + public final String getSqlType() throws DatabaseServiceException { + if (SQL_TYPE.get(field.getType()) == null) { + throw new DatabaseServiceException(columnName + " doesn't have good sql name!"); + } + return SQL_TYPE.get(field.getType()); + } + + public AnnotatedField(Field fild) { + this.field = fild; + Column column = field.getAnnotation(Column.class); + columnName = column.name(); + if (columnName.equals("")) { + columnName = field.getName(); + columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, columnName); + } + } +} diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseService.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseService.java new file mode 100644 index 0000000..88bf789 --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseService.java @@ -0,0 +1,328 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM; + +import com.google.common.base.CaseFormat; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.Column; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.PrimaryKey; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.Table; + +import java.lang.reflect.Field; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Андрей on 16.12.2015. + */ +@SuppressWarnings("Duplicates") +public class DatabaseService { + private Class typeClass; + private List columns; + private int primaryKey = -1; + private boolean hasTable = false; + private String tableName; + private static final String DATABASE_NAME = "jdbc:h2:~/azharkov"; + + public DatabaseService(Class typeClas) throws DatabaseServiceException { + columns = new ArrayList<>(); + this.typeClass = typeClas; + Table table = typeClass.getAnnotation(Table.class); + if (table == null) { + throw new DatabaseServiceException("Class must be annotated with Table"); + } + + tableName = table.name(); + if (tableName.equals("")) { + tableName = typeClass.getSimpleName(); + tableName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, tableName); + } + + for (Field field : typeClass.getDeclaredFields()) { + if (field.getAnnotation(Column.class) != null) { + columns.add(new AnnotatedField(field)); + } + if (field.getAnnotation(PrimaryKey.class) != null) { + if (field.getAnnotation(Column.class) == null) { + throw new DatabaseServiceException("Primary key must be column"); + } + if (primaryKey != -1) { + throw new DatabaseServiceException("Primary key must be only one"); + } + primaryKey = columns.size() - 1; + } + } + + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (ResultSet resultSet = connection.getMetaData().getTables(null, null, + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_UNDERSCORE, tableName), null)) { + if (resultSet.next()) { + hasTable = true; + } + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final void createTable() throws DatabaseServiceException { + if (hasTable) { + throw new DatabaseServiceException("There is table already"); + } + + StringBuilder createRequest = new StringBuilder(); + createRequest.append("CREATE TABLE ").append(tableName).append(" ("); + + for (AnnotatedField field : columns) { + createRequest.append(field.getColumnName()).append(" "); + createRequest.append(field.getSqlType()); + if (field.getField().isAnnotationPresent(PrimaryKey.class)) { + createRequest.append(" NOT NULL PRIMARY KEY"); + } + createRequest.append(", "); + } + createRequest.deleteCharAt(createRequest.lastIndexOf(",")); + createRequest.append(")"); + + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (Statement statement = connection.createStatement()) { + statement.execute(createRequest.toString()); + hasTable = true; + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final void dropTable() throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no database to drop"); + } + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (Statement statement = connection.createStatement()) { + statement.execute("DROP TABLE " + tableName); + hasTable = false; + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + //if element is not in table return null + public final T queryById(K key) throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + if (primaryKey == -1) { + throw new DatabaseServiceException("primary key should exist for this operation"); + } + if (!columns.get(primaryKey).getField().getType().isInstance(key)) { + throw new IllegalArgumentException("key should have same type as primary key"); + } + + StringBuilder query = new StringBuilder(); + query.append("SELECT * FROM ").append(tableName).append(" WHERE ") + .append(columns.get(primaryKey).getColumnName()).append(" = ?"); + + List result; + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (PreparedStatement statement = connection.prepareStatement(query.toString())) { + statement.setObject(1, key); + try (ResultSet resultSet = statement.executeQuery()) { + result = convertResult(resultSet); + } + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + if (result.size() == 0) { + return null; + } else { + return result.get(0); + } + } + + public final List queryForAll() throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + try { + return queryWithRequest("SELECT * FROM " + tableName); + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final boolean isTableCreated() { + return hasTable; + } + + public final void insert(T element) throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + StringBuilder insertRequest = new StringBuilder(); + insertRequest.append("INSERT INTO ").append(tableName).append(" ( "); + for (AnnotatedField field : columns) { + insertRequest.append(field.getColumnName()).append(", "); + } + insertRequest.deleteCharAt(insertRequest.lastIndexOf(",")); + insertRequest.append(") VALUES ("); + + for (AnnotatedField field : columns) { + insertRequest.append("?").append(", "); + } + insertRequest.deleteCharAt(insertRequest.lastIndexOf(",")); + insertRequest.append(")"); + + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (PreparedStatement statement = connection.prepareStatement(insertRequest.toString())) { + for (int i = 0; i < columns.size(); ++i) { + try { + statement.setObject(i + 1, columns.get(i).getField().get(element)); + } catch (IllegalAccessException e) { + throw new DatabaseServiceException("bad argument for insert"); + } + } + statement.execute(); + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final void update(T element) throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + if (primaryKey == -1) { + throw new DatabaseServiceException("primary key must exist for this operation"); + } + StringBuilder updateRequest = new StringBuilder(); + updateRequest.append("UPDATE ").append(tableName).append(" SET "); + for (AnnotatedField field : columns) { + updateRequest.append(field.getColumnName()).append(" = ?, "); + } + updateRequest.deleteCharAt(updateRequest.lastIndexOf(",")); + updateRequest.append(" WHERE ") + .append(columns.get(primaryKey).getColumnName()).append(" = ?"); + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (PreparedStatement statement = connection.prepareStatement(updateRequest.toString())) { + try { + for (int i = 0; i < columns.size(); ++i) { + statement.setObject(i + 1, columns.get(i).getField().get(element)); + } + statement.setObject(columns.size() + 1, columns.get(primaryKey).getField().get(element)); + } catch (IllegalAccessException e) { + throw new DatabaseServiceException("bad element for update"); + } + statement.execute(); + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final void deleteByKey(K key) throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + if (primaryKey == -1) { + throw new DatabaseServiceException("primary key must exist for this operation"); + } + if (!columns.get(primaryKey).getField().getType().isInstance(key)) { + throw new IllegalArgumentException("key should have same type as primary key"); + } + StringBuilder deleteRequest = new StringBuilder(); + deleteRequest.append("DELETE FROM ").append(tableName).append(" WHERE ") + .append(columns.get(primaryKey).getColumnName()).append(" = ?"); + + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (PreparedStatement statement = connection.prepareStatement(deleteRequest.toString())) { + statement.setObject(1, key); + statement.execute(); + } + } catch (SQLException ex) { + throw new DatabaseServiceException("Connection with database failed!", ex); + } + } + + public final void delete(T line) throws DatabaseServiceException { + if (!hasTable) { + throw new DatabaseServiceException("there is no table"); + } + Object key = null; + try { + key = columns.get(primaryKey).getField().get(line); + } catch (IllegalAccessException e) { + throw new DatabaseServiceException("bad element for delete"); + } + deleteByKey(key); + } + + private List queryWithRequest(String query) throws SQLException, DatabaseServiceException { + try (Connection connection = DriverManager.getConnection(DATABASE_NAME)) { + try (Statement statement = connection.createStatement()) { + try (ResultSet resultSet = statement.executeQuery(query)) { + return convertResult(resultSet); + } + } + } + } + + private List convertResult(ResultSet resultSet) throws SQLException, DatabaseServiceException { + List result = new ArrayList<>(); + while (resultSet.next()) { + T newElement; + try { + newElement = typeClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new DatabaseServiceException("Can not create new element"); + } + for (int i = 0; i < columns.size(); ++i) { + try { + AnnotatedField currentField = columns.get(i); + switch (currentField.getSqlType()) { + case "INT": + Long number = resultSet.getLong(i + 1); + if (currentField.getField().getType().equals(Byte.class) + || currentField.getField().getType().equals(Byte.TYPE)) { + currentField.getField().set(newElement, number.byteValue()); + } else if (currentField.getField().getType().equals(Short.class) + || currentField.getField().getType().equals(Short.TYPE)) { + currentField.getField().set(newElement, number.shortValue()); + } else if (currentField.getField().getType().equals(Integer.class) + || currentField.getField().getType().equals(Integer.TYPE)) { + currentField.getField().set(newElement, number.intValue()); + } else { + currentField.getField().set(newElement, number); + } + break; + case "DOUBLE": + Double doubleNumber = resultSet.getDouble(i + 1); + if (currentField.getField().getType().equals(Float.class) + || currentField.getField().getType().equals(Float.TYPE)) { + currentField.getField().set(newElement, doubleNumber.floatValue()); + } else { + currentField.getField().set(newElement, doubleNumber); + } + break; + case "VARCHAR(10)": + if (columns.get(i).getField().getType().equals(String.class)) { + String string = resultSet.getString(i + 1); + columns.get(i).getField().set(newElement, string); + } else { + char c = resultSet.getString(i + 1).charAt(0); + columns.get(i).getField().set(newElement, c); + } + break; + default: + throw new DatabaseServiceException("Type of field in class unsupported"); + } + } catch (IllegalAccessException e) { + throw new DatabaseServiceException("Can not initialize new element"); + } + } + result.add(newElement); + } + return result; + } +} diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceException.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceException.java new file mode 100644 index 0000000..de67040 --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceException.java @@ -0,0 +1,14 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM; + +/** + * Created by Андрей on 17.12.2015. + */ +public class DatabaseServiceException extends Exception { + DatabaseServiceException(String message) { + super(message); + } + + DatabaseServiceException(String messahe, Throwable cause) { + super(messahe, cause); + } +} diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Column.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Column.java new file mode 100644 index 0000000..6bd6ad3 --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Column.java @@ -0,0 +1,15 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Андрей on 17.12.2015. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Column { + String name() default ""; +} diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/PrimaryKey.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/PrimaryKey.java new file mode 100644 index 0000000..ba11ef1 --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/PrimaryKey.java @@ -0,0 +1,15 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Андрей on 17.12.2015. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface PrimaryKey { + +} diff --git a/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Table.java b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Table.java new file mode 100644 index 0000000..b70e244 --- /dev/null +++ b/projects/andreyzharkov/src/main/java/ru/mipt/diht/students/andreyzharkov/miniORM/annotations/Table.java @@ -0,0 +1,15 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by Андрей on 17.12.2015. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Table { + String name() default ""; +} diff --git a/projects/andreyzharkov/src/test/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceTest.java b/projects/andreyzharkov/src/test/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceTest.java new file mode 100644 index 0000000..4972cf3 --- /dev/null +++ b/projects/andreyzharkov/src/test/java/ru/mipt/diht/students/andreyzharkov/miniORM/DatabaseServiceTest.java @@ -0,0 +1,163 @@ +package ru.mipt.diht.students.andreyzharkov.miniORM; + +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.Column; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.PrimaryKey; +import ru.mipt.diht.students.andreyzharkov.miniORM.annotations.Table; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Андрей on 17.12.2015. + */ +public class DatabaseServiceTest { + List input; + DatabaseService dataBaseService; + + @Before + public void setUp() throws DatabaseServiceException { + dataBaseService = new DatabaseService<>(TestedClass.class); + if (!dataBaseService.isTableCreated()) { + dataBaseService.createTable(); + } + input = new ArrayList<>(); + List baseForInput = new ArrayList<>(); + for (int i = 0; i < 10; ++i) { + baseForInput.add(i); + } + baseForInput.forEach(element -> { + input.add(new TestedClass(element)); + }); + for (TestedClass element : input) { + dataBaseService.insert(element); + } + } + + @After + public void tryToDrop() throws DatabaseServiceException { + if (dataBaseService.isTableCreated()) { + dataBaseService.dropTable(); + } + + } + + @Test(expected = DatabaseServiceException.class) + public void testDoubleInsert() throws Exception { + dataBaseService.insert(input.get(0)); + dataBaseService.insert(input.get(0)); + } + + @Test + public void testQueryById() throws DatabaseServiceException { + TestedClass elem = dataBaseService.queryById(1); + assertThat(elem, equalTo(input.get(1))); + } + + @Test(expected = IllegalArgumentException.class) + public void testQueryByBadKey() throws Exception { + TestedClass elem = dataBaseService.queryById(1d); + } + + @Test + public void testQueryForAll() throws DatabaseServiceException { + List newList = dataBaseService.queryForAll(); + assertThat(newList, equalTo(input)); + } + + @Test + public void testDrop() throws DatabaseServiceException { + dataBaseService.dropTable(); + assertThat(dataBaseService.isTableCreated(), is(false)); + } + + @Test + public void testDelete() throws DatabaseServiceException { + dataBaseService.delete(input.get(0)); + List newList = dataBaseService.queryForAll(); + assertThat(newList, equalTo(input.subList(1, 10))); + } + + @Test + public void testInsert() throws DatabaseServiceException { + dataBaseService.delete(input.get(0)); + dataBaseService.insert(input.get(0)); + List newList = dataBaseService.queryForAll(); + assertThat(newList, equalTo(input)); + } + + @Test + public void testUpdate() throws DatabaseServiceException { + input.get(0).name = "Test"; + dataBaseService.update(input.get(0)); + List newList = dataBaseService.queryForAll(); + assertThat(newList, equalTo(input)); + } + + @Table + public static class TestedClass { + @Column + @PrimaryKey + public Integer id; + + @Column + public Short shortNumber; + + @Column + public Byte byteNumber; + + @Column + public String name; + + @Column + public Long longNumber; + + @Column + public Double doubleNumber; + + @Column + Float floatNumber; + + @Column + public Character c; + + public TestedClass(Integer id, Short shortNumber, Byte byteNumber, String name, Long longNumber, Character c, + Double doubleNumber, Float floatNumber) { + this.id = id; + this.shortNumber = shortNumber; + this.byteNumber = byteNumber; + this.name = name; + this.c = c; + this.longNumber = longNumber; + this.doubleNumber = doubleNumber; + this.floatNumber = floatNumber; + } + + public TestedClass(Integer i) { + this(i, i.shortValue(), i.byteValue(), i.toString(), + i.longValue(), i.toString().charAt(0), i.doubleValue(), i.floatValue()); + } + + public TestedClass() { + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TestedClass)) { + return false; + } + TestedClass another = (TestedClass) obj; + return id.equals(another.id) && shortNumber.equals(another.shortNumber) + && byteNumber.equals(another.byteNumber) && name.equals(another.name) + && longNumber.equals(another.longNumber); + } + } + +} diff --git a/projects/checkstyle.xml b/projects/checkstyle.xml index 3401cf5..8a0c1f9 100644 --- a/projects/checkstyle.xml +++ b/projects/checkstyle.xml @@ -107,8 +107,9 @@ - - + + + diff --git a/projects/pom.xml b/projects/pom.xml index 5d14966..b8f00b4 100644 --- a/projects/pom.xml +++ b/projects/pom.xml @@ -29,7 +29,9 @@ dkhurtin ale3otik - Pitovsky + Pitovsky + andreyzharkov +