Skip to content

Commit 150b950

Browse files
committed
GP-121 copy exercise from completed
1 parent de138cd commit 150b950

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=50/>Account db initializer exercise :muscle:
2+
Improve your database design and SQL skills
3+
### Task
4+
`AccountDbInitializer` provides an API that allows to create(initialize) a database (one table). It contains a *javadoc*
5+
that specifies database requirements. `AccountDbInitializer` has a field `DataSource`. Your job is to use that
6+
`dataSource` and **implement the todo section**. E.g. implement the method `init()` that should **create an account db.**
7+
8+
The purpose of the task is to **design a database table following docs and naming convention and implement it using SQL and
9+
JDBC API.**
10+
11+
To verify your implementation, run `AccountDbInitializerTest.java`
12+
13+
### Pre-conditions :heavy_exclamation_mark:
14+
You're supposed to be familiar with database design and naming convention, *JDBC API* and *SQL*
15+
16+
### How to start :question:
17+
* Just clone the repository and start implementing the **todo** section, verify your changes by running tests
18+
* If you don't have enough knowledge about this domain, check out the [links below](#related-materials-information_source)
19+
* Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation
20+
21+
### Related materials :information_source:
22+
* [JDBC API basics tutorial](https://github.com/bobocode-projects/jdbc-api-tutorial/tree/master/jdbc-basics) <img src="https://raw.githubusercontent.com/bobocode-projects/resources/master/image/logo_transparent_background.png" height=20/>
23+
24+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>2-0-jdbc-api</artifactId>
7+
<groupId>com.bobocode</groupId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>2-0-2-create-table</artifactId>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>com.bobocode</groupId>
17+
<artifactId>jdbc-util</artifactId>
18+
<version>1.0-SNAPSHOT</version>
19+
</dependency>
20+
</dependencies>
21+
22+
</project>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.bobocode;
2+
3+
import javax.sql.DataSource;
4+
import java.sql.Connection;
5+
import java.sql.SQLException;
6+
import java.sql.Statement;
7+
8+
/**
9+
* {@link AccountDbInitializer} provides an API that allow to initialize (create) an Account table in the database
10+
*/
11+
public class AccountDbInitializer {
12+
private DataSource dataSource;
13+
14+
public AccountDbInitializer(DataSource dataSource) {
15+
this.dataSource = dataSource;
16+
}
17+
18+
/**
19+
* Creates an {@code account} table. That table has a identifier column {@code id} with type {@code bigint}.
20+
* It also contains an {@code email} column that is mandatory and should have unique value. This column is be able
21+
* to store any valid email. The table also has columns {@code first_name}, {@code last_name}, and {@code gender}
22+
* that are typical string columns with 255 characters, and are mandatory. Account {@code birthday} is stored
23+
* in the {@code DATE} mandatory column. The value of account balance is not mandatory, and is stored
24+
* in the {@code balance} column that is a {@code DECIMAL} number with {@code precision = 19} ,
25+
* and {@code scale = 4}. A column {@code creation_time} stores a {@code TIMESTAMP}, is mandatory, and has a default
26+
* value that is set to the current timestamp using database function {@code now()}. Table primary key
27+
* is an {@code id}, and corresponding constraint is named {@code "account_pk"}. An unique constraint that
28+
* is created for {@code email column} is called "account_email_uq"
29+
*
30+
* @throws SQLException
31+
*/
32+
public void init() throws SQLException {
33+
try (Connection connection = dataSource.getConnection()) {
34+
Statement statement = connection.createStatement();
35+
statement.execute("CREATE TABLE account(" +
36+
"id BIGINT," +
37+
"email VARCHAR(255) NOT NULL," +
38+
"first_name VARCHAR(255) NOT NULL," +
39+
"last_name VARCHAR(255) NOT NULL," +
40+
"gender VARCHAR(255) NOT NULL," +
41+
"birthday DATE NOT NULL," +
42+
"balance DECIMAL(19,4)," +
43+
"creation_time TIMESTAMP NOT NULL DEFAULT now()," +
44+
"CONSTRAINT account_pk PRIMARY KEY (id)," +
45+
"CONSTRAINT account_email_uq UNIQUE (email)" +
46+
");");
47+
}
48+
}
49+
}
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
package com.bobocode;
2+
3+
import com.bobocode.util.JdbcUtil;
4+
import org.junit.jupiter.api.BeforeAll;
5+
import org.junit.jupiter.api.Test;
6+
7+
import javax.sql.DataSource;
8+
import java.sql.Connection;
9+
import java.sql.ResultSet;
10+
import java.sql.SQLException;
11+
import java.sql.Statement;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
17+
18+
19+
public class AccountDbInitializerTest {
20+
private static DataSource dataSource;
21+
22+
@BeforeAll
23+
static void init() throws SQLException {
24+
dataSource = JdbcUtil.createDefaultInMemoryH2DataSource();
25+
AccountDbInitializer dbInitializer = new AccountDbInitializer(dataSource);
26+
dbInitializer.init();
27+
}
28+
29+
@Test
30+
void testTableHasCorrectName() throws SQLException {
31+
try (Connection connection = dataSource.getConnection()) {
32+
Statement statement = connection.createStatement();
33+
34+
ResultSet resultSet = statement.executeQuery("SHOW TABLES");
35+
resultSet.next();
36+
String tableName = resultSet.getString("table_name");
37+
38+
assertEquals("account", tableName);
39+
}
40+
}
41+
42+
@Test
43+
void testTableHasPrimaryKey() throws SQLException {
44+
try (Connection connection = dataSource.getConnection()) {
45+
Statement statement = connection.createStatement();
46+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.CONSTRAINTS" +
47+
" WHERE table_name = 'account' AND constraint_type = 'PRIMARY_KEY';");
48+
49+
boolean resultIsNotEmpty = resultSet.next();
50+
51+
assertTrue(resultIsNotEmpty);
52+
}
53+
}
54+
55+
@Test
56+
void testPrimaryKeyHasCorrectName() throws SQLException {
57+
try (Connection connection = dataSource.getConnection()) {
58+
Statement statement = connection.createStatement();
59+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.CONSTRAINTS" +
60+
" WHERE table_name = 'account' AND constraint_type = 'PRIMARY_KEY';");
61+
62+
resultSet.next();
63+
String pkConstraintName = resultSet.getString("constraint_name");
64+
65+
assertEquals("account_pk", pkConstraintName);
66+
}
67+
}
68+
69+
@Test
70+
void testPrimaryKeyBasedOnIdField() throws SQLException {
71+
try (Connection connection = dataSource.getConnection()) {
72+
Statement statement = connection.createStatement();
73+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.CONSTRAINTS" +
74+
" WHERE table_name = 'account' AND constraint_type = 'PRIMARY_KEY';");
75+
76+
resultSet.next();
77+
String pkColumn = resultSet.getString("column_list");
78+
79+
assertEquals("id", pkColumn);
80+
}
81+
}
82+
83+
@Test
84+
void testTableHasCorrectAlternativeKey() throws SQLException {
85+
try (Connection connection = dataSource.getConnection()) {
86+
Statement statement = connection.createStatement();
87+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.CONSTRAINTS" +
88+
" WHERE table_name = 'account' AND constraint_type = 'UNIQUE';");
89+
90+
resultSet.next();
91+
String uniqueConstraintName = resultSet.getString("constraint_name");
92+
String uniqueConstraintColumn = resultSet.getString("column_list");
93+
94+
assertEquals("account_email_uq", uniqueConstraintName);
95+
assertEquals("email", uniqueConstraintColumn);
96+
}
97+
}
98+
99+
@Test
100+
void testTableHasAllRequiredColumns() throws SQLException {
101+
try (Connection connection = dataSource.getConnection()) {
102+
Statement statement = connection.createStatement();
103+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
104+
" WHERE table_name = 'account';");
105+
106+
List<String> columns = fetchColumnsNames(resultSet);
107+
108+
assertEquals(8, columns.size());
109+
assertTrue(columns.containsAll(List.of("id", "first_name", "last_name", "email", "gender", "balance", "birthday", "creation_time")));
110+
}
111+
}
112+
113+
private List<String> fetchColumnsNames(ResultSet resultSet) throws SQLException {
114+
List<String> columns = new ArrayList<>();
115+
while (resultSet.next()) {
116+
String columnName = resultSet.getString("column_name");
117+
columns.add(columnName);
118+
}
119+
return columns;
120+
}
121+
122+
123+
@Test
124+
void testRequiredColumnsHaveHaveNotNullConstraint() throws SQLException {
125+
try (Connection connection = dataSource.getConnection()) {
126+
Statement statement = connection.createStatement();
127+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
128+
" WHERE table_name = 'account' AND nullable = false;");
129+
130+
List<String> notNullColumns = fetchColumnsNames(resultSet);
131+
132+
assertEquals(7, notNullColumns.size());
133+
assertTrue(notNullColumns.containsAll(List.of("id", "first_name", "last_name", "email", "gender", "birthday", "creation_time")));
134+
}
135+
}
136+
137+
@Test
138+
void testIdHasTypeBiInteger() throws SQLException {
139+
try (Connection connection = dataSource.getConnection()) {
140+
Statement statement = connection.createStatement();
141+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
142+
" WHERE table_name = 'account' AND column_name = 'id';");
143+
144+
resultSet.next();
145+
String idTypeName = resultSet.getString("type_name");
146+
147+
assertEquals("BIGINT", idTypeName);
148+
}
149+
}
150+
151+
@Test
152+
void testCreationTimeHasTypeTimestamp() throws SQLException {
153+
try (Connection connection = dataSource.getConnection()) {
154+
Statement statement = connection.createStatement();
155+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
156+
" WHERE table_name = 'account' AND column_name = 'creation_time';");
157+
158+
resultSet.next();
159+
String creationTimeColumnType = resultSet.getString("type_name");
160+
161+
assertEquals("TIMESTAMP", creationTimeColumnType);
162+
}
163+
}
164+
165+
@Test
166+
void testCreationTimeHasDefaultValue() throws SQLException {
167+
try (Connection connection = dataSource.getConnection()) {
168+
Statement statement = connection.createStatement();
169+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
170+
" WHERE table_name = 'account' AND column_name = 'creation_time';");
171+
172+
resultSet.next();
173+
String creationTimeColumnDefault = resultSet.getString("column_default");
174+
175+
assertEquals("NOW()", creationTimeColumnDefault);
176+
}
177+
}
178+
179+
@Test
180+
void testEmailColumnHasCorrectSize() throws SQLException {
181+
try (Connection connection = dataSource.getConnection()) {
182+
Statement statement = connection.createStatement();
183+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
184+
" WHERE table_name = 'account' AND column_name = 'email';");
185+
186+
resultSet.next();
187+
String emailColumnType = resultSet.getString("type_name");
188+
int emailColumnMaxLength = resultSet.getInt("character_maximum_length");
189+
190+
assertEquals("VARCHAR", emailColumnType);
191+
assertEquals(255, emailColumnMaxLength);
192+
}
193+
}
194+
195+
@Test
196+
void testBirthdayColumnHasCorrectType() throws SQLException {
197+
try (Connection connection = dataSource.getConnection()) {
198+
Statement statement = connection.createStatement();
199+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
200+
" WHERE table_name = 'account' AND column_name = 'birthday';");
201+
202+
resultSet.next();
203+
String birthdayColumnType = resultSet.getString("type_name");
204+
205+
assertEquals("DATE", birthdayColumnType);
206+
}
207+
}
208+
209+
@Test
210+
void testBalanceColumnHasCorrectType() throws SQLException {
211+
try (Connection connection = dataSource.getConnection()) {
212+
Statement statement = connection.createStatement();
213+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
214+
" WHERE table_name = 'account' AND column_name = 'balance';");
215+
216+
resultSet.next();
217+
String balanceColumnType = resultSet.getString("type_name");
218+
int balanceColumnPrecision = resultSet.getInt("numeric_precision");
219+
int balanceColumnScale = resultSet.getInt("numeric_scale");
220+
221+
assertEquals("DECIMAL", balanceColumnType);
222+
assertEquals(19, balanceColumnPrecision);
223+
assertEquals(4, balanceColumnScale);
224+
}
225+
}
226+
227+
@Test
228+
void testBalanceIsNotMandatory() throws SQLException {
229+
try (Connection connection = dataSource.getConnection()) {
230+
Statement statement = connection.createStatement();
231+
ResultSet resultSet = statement.executeQuery("SELECT * FROM INFORMATION_SCHEMA.COLUMNS" +
232+
" WHERE table_name = 'account' AND column_name = 'balance';");
233+
234+
resultSet.next();
235+
boolean balanceColumnIsNullable = resultSet.getBoolean("nullable");
236+
237+
assertTrue(balanceColumnIsNullable);
238+
}
239+
}
240+
241+
@Test
242+
void testStringColumnsHaveCorrectTypeAndLength() throws SQLException {
243+
try (Connection connection = dataSource.getConnection()) {
244+
Statement statement = connection.createStatement();
245+
ResultSet resultSet = statement.executeQuery("SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS" +
246+
" WHERE table_name = 'account' AND type_name = 'VARCHAR' AND character_maximum_length = 255;");
247+
248+
List<String> stringColumns = fetchColumnsNames(resultSet);
249+
250+
assertEquals(4, stringColumns.size());
251+
assertTrue(stringColumns.containsAll(List.of("first_name", "last_name", "email", "gender")));
252+
}
253+
}
254+
255+
256+
}

2-0-jdbc-api/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<packaging>pom</packaging>
1414

1515
<modules>
16+
<module>2-0-2-create-table</module>
1617
<module>2-1-1-product-dao</module>
1718
</modules>
1819

0 commit comments

Comments
 (0)