diff --git a/config/init_db.sql b/config/init_db.sql index eefa5a52..b307f888 100644 --- a/config/init_db.sql +++ b/config/init_db.sql @@ -14,3 +14,14 @@ create table contact create unique index contact_uuid_type_index on contact (resume_uuid, type); + +create table section +( + id serial primary key, + resume_uuid char(36) not null references resume (uuid) on delete cascade, + type text not null, + value text not null +); + +create unique index section_idx + on section (resume_uuid, type); \ No newline at end of file diff --git a/src/ru/javawebinar/basejava/storage/SqlStorage.java b/src/ru/javawebinar/basejava/storage/SqlStorage.java index dc61fd87..f376c05b 100644 --- a/src/ru/javawebinar/basejava/storage/SqlStorage.java +++ b/src/ru/javawebinar/basejava/storage/SqlStorage.java @@ -12,7 +12,10 @@ import ru.javawebinar.basejava.exception.NotExistStorageException; import ru.javawebinar.basejava.model.ContactType; import ru.javawebinar.basejava.model.Resume; +import ru.javawebinar.basejava.model.Section; +import ru.javawebinar.basejava.model.SectionType; import ru.javawebinar.basejava.sql.SqlHelper; +import ru.javawebinar.basejava.util.JsonParser; public class SqlStorage implements Storage { @@ -38,7 +41,9 @@ public void update(Resume r) { throw new NotExistStorageException(r.getUuid()); } deleteContacts(conn, r); + deleteSections(conn, r); insertContacts(conn, r); + insertSections(conn, r); return null; } }); @@ -54,32 +59,48 @@ public void save(Resume r) { ps.execute(); } insertContacts(conn, r); + insertSections(conn, r); return null; }); } @Override public Resume get(String uuid) { - return sqlHelper.execute("SELECT * FROM resume r " + - " LEFT JOIN contact c " + - " ON r.uuid = c.resume_uuid " + - " WHERE r.uuid = ?", ps -> { - ps.setString(1, uuid); - ResultSet rs = ps.executeQuery(); - if (!rs.next()) { - throw new NotExistStorageException(uuid); + return sqlHelper.transactionalExecute(conn -> { + Resume r; + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM resume WHERE uuid = ?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + throw new NotExistStorageException(uuid); + } + r = new Resume(uuid, rs.getString("full_name")); } - Resume resume = new Resume(uuid, rs.getString("full_name")); - do { - addContact(rs, resume); - } while (rs.next()); - return resume; + + try (PreparedStatement ps = conn.prepareStatement( + "SELECT * FROM contact WHERE resume_uuid = ?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addContact(rs, r); + } + } + + try (PreparedStatement ps = conn.prepareStatement( + "SELECT * FROM section WHERE resume_uuid = ?")) { + ps.setString(1, uuid); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + addSection(rs, r); + } + } + return r; }); } @Override public void delete(String uuid) { - sqlHelper.execute("DELETE FROM resume WHERE uuid = ?", ps -> { + sqlHelper.execute("DELETE FROM resume WHERE uuid = ?", ps -> { ps.setString(1, uuid); if (ps.executeUpdate() == 0) { throw new NotExistStorageException(uuid); @@ -90,22 +111,34 @@ public void delete(String uuid) { @Override public List getAllSorted() { - return sqlHelper.execute("SELECT * FROM resume r " + - " LEFT JOIN contact c " + - " ON r.uuid = c.resume_uuid" + - " ORDER BY full_name, uuid", ps -> { - ResultSet rs = ps.executeQuery(); - Map map = new LinkedHashMap<>(); - while (rs.next()) { - String uuid = rs.getString("uuid"); - Resume r = map.get(uuid); - if (r == null) { - r = new Resume(uuid, rs.getString("full_name")); - map.put(uuid, r); + return sqlHelper.transactionalExecute(conn -> { + Map resumes = new LinkedHashMap<>(); + + try (PreparedStatement ps = conn.prepareStatement( + "SELECT * FROM resume ORDER BY full_name, uuid")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + String uuid = rs.getString("uuid"); + resumes.put(uuid, new Resume(uuid, rs.getString("full_name"))); + } + } + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM contact")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Resume r = resumes.get(rs.getString("resume_uuid")); + addContact(rs, r); + } + } + + try (PreparedStatement ps = conn.prepareStatement("SELECT * FROM section")) { + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Resume r = resumes.get(rs.getString("resume_uuid")); + addSection(rs, r); } - addContact(rs, r); } - return new ArrayList<>(map.values()); + return new ArrayList<>(resumes.values()); }); } @@ -117,12 +150,19 @@ public int size() { }); } - private void deleteContacts(Connection conn, Resume r) { - sqlHelper.execute("DELETE FROM contact WHERE resume_uuid = ?", ps -> { + private void deleteContacts(Connection conn, Resume r) throws SQLException { + deleteAttributes(conn, r, "DELETE FROM contact WHERE resume_uuid = ?"); + } + + private void deleteSections(Connection conn, Resume r) throws SQLException { + deleteAttributes(conn, r, "DELETE FROM section WHERE resume_uuid = ?"); + } + + private void deleteAttributes(Connection conn, Resume r, String query) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement(query)) { ps.setString(1, r.getUuid()); ps.execute(); - return null; - }); + } } private void insertContacts(Connection conn, Resume r) throws SQLException { @@ -138,10 +178,32 @@ private void insertContacts(Connection conn, Resume r) throws SQLException { } } + private void insertSections(Connection conn, Resume r) throws SQLException { + try (PreparedStatement ps = conn.prepareStatement + ("INSERT INTO section (resume_uuid, type, value) VALUES (?,?,?)")) { + for (Map.Entry e : r.getSections().entrySet()) { + ps.setString(1, r.getUuid()); + ps.setString(2, e.getKey().name()); + Section section = e.getValue(); + ps.setString(3, JsonParser.write(section, Section.class)); + ps.addBatch(); + } + ps.executeBatch(); + } + } + private void addContact(ResultSet rs, Resume r) throws SQLException { String value = rs.getString("value"); if (value != null) { r.addContact(ContactType.valueOf(rs.getString("type")), value); } } + + private void addSection(ResultSet rs, Resume r) throws SQLException { + String value = rs.getString("value"); + if (value != null) { + SectionType type = SectionType.valueOf(rs.getString("type")); + r.addSection(type, JsonParser.read(value, Section.class)); + } + } } diff --git a/src/ru/javawebinar/basejava/util/JsonParser.java b/src/ru/javawebinar/basejava/util/JsonParser.java index 4f0438f0..e29aed5a 100644 --- a/src/ru/javawebinar/basejava/util/JsonParser.java +++ b/src/ru/javawebinar/basejava/util/JsonParser.java @@ -9,7 +9,7 @@ public class JsonParser { private static Gson GSON = new GsonBuilder() - .registerTypeAdapter(Section.class, new JsonSectionAdapter<>()) + .registerTypeAdapter(Section.class, new JsonSectionAdapter()) .create(); public static T read(Reader reader, Class clazz) { @@ -19,4 +19,16 @@ public static T read(Reader reader, Class clazz) { public static void write(T object, Writer writer) { GSON.toJson(object, writer); } + + public static T read(String content, Class clazz) { + return GSON.fromJson(content, clazz); + } + + public static String write(T object) { + return GSON.toJson(object); + } + + public static String write(T object, Class clazz) { + return GSON.toJson(object, clazz); + } } diff --git a/test/ru/javawebinar/basejava/TestData.java b/test/ru/javawebinar/basejava/TestData.java new file mode 100644 index 00000000..558ae781 --- /dev/null +++ b/test/ru/javawebinar/basejava/TestData.java @@ -0,0 +1,63 @@ +package ru.javawebinar.basejava; + +import java.time.Month; +import java.util.UUID; +import ru.javawebinar.basejava.model.ContactType; +import ru.javawebinar.basejava.model.Organization; +import ru.javawebinar.basejava.model.Resume; +import ru.javawebinar.basejava.model.SectionLine; +import ru.javawebinar.basejava.model.SectionList; +import ru.javawebinar.basejava.model.SectionOrganization; +import ru.javawebinar.basejava.model.SectionType; + +public class TestData { + + public static final String UUID_1 = UUID.randomUUID().toString(); + public static final String UUID_2 = UUID.randomUUID().toString(); + public static final String UUID_3 = UUID.randomUUID().toString(); + public static final String UUID_4 = UUID.randomUUID().toString(); + public static final Resume R1; + public static final Resume R2; + public static final Resume R3; + public static final Resume R4; + + static { + R1 = new Resume(UUID_1, "Name1"); + R2 = new Resume(UUID_2, "Name2"); + R3 = new Resume(UUID_3, "Name3"); + R4 = new Resume(UUID_4, "Name4"); + + R1.addContact(ContactType.MOBILE_PHONE, "+79001234567"); + R1.addContact(ContactType.EMAIL, "abc@mail.ru"); + R4.addContact(ContactType.SKYPE, "Skype"); + R4.addContact(ContactType.MOBILE_PHONE, "+76666666666"); + + R1.addSection(SectionType.OBJECTIVE, new SectionLine("Objective")); + R1.addSection(SectionType.PERSONAL, new SectionLine("Personal data")); + R1.addSection(SectionType.ACHIEVEMENT, + new SectionList("Achievement11", "Achievement12", "Achievement13")); + R1.addSection(SectionType.QUALIFICATION, new SectionList("Java", "git", "SQL")); + R1.addSection(SectionType.EXPERIENCE, + new SectionOrganization( + new Organization("Organization11", "http://organization11.ru", + new Organization.Position(2015, Month.JANUARY, "position11", "content11"), + new Organization.Position(2010, Month.JULY, 2014, Month.DECEMBER, "position12", + "content12")))); + R1.addSection(SectionType.EDUCATION, + new SectionOrganization( + new Organization("university", null, + new Organization.Position(2005, Month.SEPTEMBER, 2010, Month.JUNE, "aspirant", + null), + new Organization.Position(2001, Month.SEPTEMBER, 2005, Month.APRIL, "student", + "IT")), + new Organization("Organization12", "http://organization12.ru"))); + + R2.addContact(ContactType.MOBILE_PHONE, "1234567"); + R2.addContact(ContactType.SKYPE, "@SecondMember"); + R2.addSection(SectionType.EXPERIENCE, + new SectionOrganization( + new Organization("Organization21", "http://organization21.ru", + new Organization.Position(2018, Month.JULY, "position21", "content21")))); + } + +} \ No newline at end of file diff --git a/test/ru/javawebinar/basejava/storage/AbstractStorageTest.java b/test/ru/javawebinar/basejava/storage/AbstractStorageTest.java index fd4bb5b1..1112eef3 100644 --- a/test/ru/javawebinar/basejava/storage/AbstractStorageTest.java +++ b/test/ru/javawebinar/basejava/storage/AbstractStorageTest.java @@ -1,12 +1,12 @@ package ru.javawebinar.basejava.storage; +import static ru.javawebinar.basejava.TestData.*; + import java.io.File; -import java.time.Month; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -25,55 +25,6 @@ protected AbstractStorageTest(Storage storage) { this.storage = storage; } - private static final String UUID_1 = UUID.randomUUID().toString(); - private static final String UUID_2 = UUID.randomUUID().toString(); - private static final String UUID_3 = UUID.randomUUID().toString(); - private static final String UUID_4 = UUID.randomUUID().toString(); - private static final Resume R1; - private static final Resume R2; - private static final Resume R3; - private static final Resume R4; - - static { - R1 = new Resume(UUID_1, "Name1"); - R2 = new Resume(UUID_2, "Name2"); - R3 = new Resume(UUID_3, "Name3"); - R4 = new Resume(UUID_4, "Name4"); - - R1.addContact(ContactType.MOBILE_PHONE, "+79001234567"); - R1.addContact(ContactType.EMAIL, "abc@mail.ru"); - R4.addContact(ContactType.SKYPE, "Skype"); - R4.addContact(ContactType.MOBILE_PHONE, "+76666666666"); - - -// R1.addSection(SectionType.OBJECTIVE, new SectionLine("Objective")); -// R1.addSection(SectionType.PERSONAL, new SectionLine("Personal data")); -// R1.addSection(SectionType.ACHIEVEMENT, -// new SectionList("Achievement11", "Achievement12", "Achievement13")); -// R1.addSection(SectionType.QUALIFICATION, new SectionList("Java", "git", "SQL")); -// R1.addSection(SectionType.EXPERIENCE, -// new SectionOrganization( -// new Organization("Organization11", "http://organization11.ru", -// new Organization.Position(2015, Month.JANUARY, "position11", "content11"), -// new Organization.Position(2010, Month.JULY, 2014, Month.DECEMBER, "position12", -// "content12")))); -// R1.addSection(SectionType.EDUCATION, -// new SectionOrganization( -// new Organization("university", null, -// new Organization.Position(2005, Month.SEPTEMBER, 2010, Month.JUNE, "aspirant", -// null), -// new Organization.Position(2001, Month.SEPTEMBER, 2005, Month.APRIL, "student", -// "IT")), -// new Organization("Organization12", "http://organization12.ru"))); -// -// R2.addContact(ContactType.MOBILE_PHONE, "1234567"); -// R2.addContact(ContactType.SKYPE, "@SecondMember"); -// R2.addSection(SectionType.EXPERIENCE, -// new SectionOrganization( -// new Organization("Organization21", "http://organization21.ru", -// new Organization.Position(2018, Month.JULY, "position21", "content21")))); - } - @BeforeEach public void setUp() { storage.clear(); diff --git a/test/ru/javawebinar/basejava/util/JsonParserTest.java b/test/ru/javawebinar/basejava/util/JsonParserTest.java new file mode 100644 index 00000000..86dc94ed --- /dev/null +++ b/test/ru/javawebinar/basejava/util/JsonParserTest.java @@ -0,0 +1,30 @@ +package ru.javawebinar.basejava.util; + +import static org.junit.jupiter.api.Assertions.*; +import static ru.javawebinar.basejava.TestData.R1; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import ru.javawebinar.basejava.model.Resume; +import ru.javawebinar.basejava.model.Section; +import ru.javawebinar.basejava.model.SectionLine; + +class JsonParserTest { + + @Test + void testResume() { + String json = JsonParser.write(R1); + System.out.println(json); + Resume resume = JsonParser.read(json, Resume.class); + Assertions.assertEquals(R1, resume); + } + + @Test + void testSection() { + Section section = new SectionLine("Personal1"); + String json = JsonParser.write(section, Section.class); + System.out.println(json); + Section section2 = JsonParser.read(json, Section.class); + Assertions.assertEquals(section, section2); + } +} \ No newline at end of file