Restructure database code.
[cfb.git] / prod / net / jaekl / cfb / db / Schema.java
1 package net.jaekl.cfb.db;
2
3 // Copyright (C) 2015 Christian Jaekl
4
5 import java.sql.Connection;
6 import java.sql.DatabaseMetaData;
7 import java.sql.ResultSet;
8 import java.sql.SQLException;
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import java.util.Locale;
12
13 import net.jaekl.cfb.db.driver.DbDriver;
14
15 public class Schema {
16         String m_name;
17         DbDriver m_driver;
18         ArrayList<Table> m_tables;
19         ArrayList<Sequence> m_sequences;
20         
21         public Schema(String name, DbDriver driver) {
22                 m_name = name;
23                 m_driver = driver;
24                 m_tables = new ArrayList<Table>();
25                 m_sequences = new ArrayList<Sequence>();
26         }
27         
28         public boolean ensureDbInitialized(Connection con) throws SQLException {
29                 assert(null != con);
30                 
31                 if (allTablesPresent(con)) {
32                         return true;
33                 }
34                 
35                 if (!createAllTables(con)) {
36                         return false;
37                 }
38                 
39                 if (!createAllSequences(con)) { 
40                         return false;
41                 }
42                 
43                 if (!postCreationInit(con)) {
44                         
45                 }
46                 
47                 return true;
48         }
49         
50         boolean postCreationInit(Connection con) throws SQLException {
51                 // no-op
52                 // Override this in a derived class if you need to initialize something 
53                 // after the tables and sequences are created.
54                 return true;
55         }
56         
57         boolean allTablesPresent(Connection con) throws SQLException 
58         {
59                 assert(null != con);
60                 
61                 DatabaseMetaData dbmd = con.getMetaData();
62                 HashSet<String> extantTables = new HashSet<String>();
63                 
64                 try (ResultSet rs = dbmd.getTables(null, null, null, new String[]{"TABLE"})) {
65                         while (rs.next()) {
66                                 extantTables.add(rs.getString(3).toUpperCase(Locale.CANADA));
67                         }
68                 }
69                 
70                 for (Table table : m_tables) {
71                         String name = table.getName().toUpperCase(Locale.CANADA);
72                         if ( ! extantTables.contains(name) ) {
73                                 // One or more tables missing
74                                 return false;
75                         }
76                 }
77                 
78                 // We could be more thorough here, and check that the expected columns are in place.
79                 // Also, eventually, some sort of DB schema versioning will be needed.
80                 
81                 return true;            
82         }
83         
84         boolean createAllTables(Connection con) throws SQLException {
85                 for (Table table : m_tables) {
86                         if (!m_driver.createTable(con, table)) {
87                                 return false;
88                         }
89                 }
90                 return true;
91         }
92         
93         boolean createAllSequences(Connection con) throws SQLException {
94                 for (Sequence seq : m_sequences) {
95                         if (!m_driver.createSequence(con, seq)) {
96                                 return false;
97                         }
98                 }
99                 return true;
100         }
101         
102         void addTable(Table table) {
103                 m_tables.add(table);
104         }
105         
106         // Add a list of tables.
107         // Define each table in the list as follows:
108         // {
109         //   { table_name },
110         //   { column_name, type, width (-1 for default), null/not_null }
111         // }
112         void addTables(Table[] tables) 
113         {
114                 for (Table table : tables) {
115                         addTable(table);
116                 }
117         }
118         
119         void addSequence(Sequence seq) {
120                 m_sequences.add(seq);
121         }
122         
123         void addSequences(Sequence[] sequences) {
124                 for (Sequence sequence : sequences) {
125                         addSequence(sequence);
126                 }
127         }
128 }