source: gs2-extensions/tipple-bridge/trunk/src/src/main/java/sqlCli.java@ 34905

Last change on this file since 34905 was 34905, checked in by davidb, 3 years ago

Code tidy up; added in example SQL statement for next planned stage of work, which could retrieve files (e.g. photos) associated with content items

File size: 9.8 KB
Line 
1import com.mysql.cj.exceptions.MysqlErrorNumbers;
2import picocli.CommandLine;
3import picocli.CommandLine.Command;
4import picocli.CommandLine.Option;
5import picocli.CommandLine.Parameters;
6
7import java.sql.*;
8import java.util.HashMap;
9import com.google.gson.Gson;
10import java.io.FileWriter;
11import java.util.concurrent.Callable;
12
13/*
14TODO:
15
16 */
17
18//-u su41Tipple -p --port 3306 --dbname koicarp_test
19//@/home/su41/configFileTest
20
21/*
22The mixinStandardHelpOptions = true adds in --help and --version options
23 */
24@Command (name="sqlCli", description = "Arguments for database connection", mixinStandardHelpOptions = true)
25public class sqlCli implements Callable<Integer> {
26
27 //our cli arguments
28 @Option(names = {"-u", "--user"}, description = "The user name",
29 required = true)
30 String userName;
31
32 //interactive allows the user to omit the argument value and instead enter it as a prompt
33 /*
34 NOTE: if you are running an interactive option on an IDE, most likely the IDE will be using
35 the javaw.exe library which tries to mimic the terminal.
36 The point is that interactive option would usually make whatever you enter hidden (it doesn't
37 echo back) but on the IDE it will echo back.
38 */
39 @Option(names = {"-p", "--password"}, description = "User's password", interactive = true,
40 required = true)
41 String password;
42
43 @Option(names = {"--port"}, description="Port number", defaultValue="3306")
44 int port;
45
46 @Option(names = {"--dbname"}, description="Name of the database", defaultValue="koicarp_test")
47 String dbName;
48
49 //project id - it should be required right?
50 //if they omit, the query would come up empty.
51 @Option(names = {"--projectID"}, description = "ID number of the project")
52 String projectID;
53
54 @Option(names = {"--host"}, description = "Type of host e.g. localhost ",
55 required = true, defaultValue = "localhost")
56 String host;
57
58 /*
59 this parameter can be read in at any point on the options line.
60 but that's only because it's the only parameter at the moment.
61 If we add more parameters, will have to start indexing
62 https://picocli.info/quick-guide.html#_options_and_parameters
63 */
64 @Parameters(description = "Name of the output JSON file. E.g. yourFileName.json")
65 String jsonOutputFile;
66
67 public Integer call(){
68 //if it doesn't end with ...
69 if(!jsonOutputFile.endsWith(".json")){
70 System.out.println("Invalid file format.");
71 System.out.println("The file to save the output to must be a .json file.");
72 //exit code other than 0 means it's not fine.
73 return 1;
74 }
75 //else we're good to go.
76 // databaseConnect(userName, password, port, dbName, projectID, jsonOutputFile, host);
77 int status = databaseConnect(userName, password, port, dbName, projectID, jsonOutputFile, host);
78 return status;
79 }
80
81 public static void main(String... args) {
82 int exitCode = new CommandLine(new sqlCli()).execute(args);
83 System.exit(exitCode);
84 }
85
86
87 private static int databaseConnect(String user, String pass, int port, String dbName,
88 String projectID, String fileName, String host){
89 Connection conn;
90 Statement stmt;
91 //return status for code to send back to commandLine.execute
92 int returnStatus = 0;
93 // String dbUrl = "jdbc:mysql://localhost:" + port +"/"+ dbName;
94
95 //TODO: are we changing / putting the type of database into a variable too?
96 String dbUrl = "jdbc:mysql://" + host + ":" + port +"/"+ dbName;
97
98 //originally in the try ()
99 // FileWriter file = new FileWriter("tippleData.json")
100 System.out.println("Saving output to:");
101 System.out.println(" " + fileName);
102
103 try (FileWriter file = new FileWriter(fileName)){
104 //register the jdbc driver
105 Class.forName("com.mysql.cj.jdbc.Driver");
106 //opening the connection
107 System.out.println("Connecting to database:");
108 System.out.println(" " + dbUrl);
109
110 conn = DriverManager.getConnection(dbUrl, user, pass);
111
112 //create and execute query
113 stmt = conn.createStatement();
114 //remember to put in the projectId parameter instead of hardcoding it
115
116
117 /* Step 1: retrieve all the content items associated with the given projectId */
118
119 String sql = "SELECT name, id, typeId, created from td_ContentItem where projectId="+projectID;
120
121 // consider usingLIMIT to get the returned results in controled batch size, rather than getting
122 // all the results at once.
123
124 ResultSet myResult = stmt.executeQuery(sql);
125 ResultSetMetaData resultMetaData = myResult.getMetaData();
126
127 int dbColumn = resultMetaData.getColumnCount();
128 System.out.println(" Number of columns in table retrieved: " + dbColumn);
129
130
131 /* Step 2: Iterate through each content item, and retrieve the media files that content item has */
132
133 /* The following is an example SELECT statement (for a hard-wired contentId = 333) that retrieves all the
134 images (both the original name, and the hashed 'path' that maps to the media files on the file
135 system (the contentItem itself at the time of writing was in projectID=4) */
136
137 /*
138SELECT mFile.filename, mFile.path
139
140FROM td_ManagedFile AS mFile, td_ContentItem AS cItem, td_ContentItem_MediaItem AS cMediaItem, td_MediaItem AS mItem
141
142WHERE
143
144cItem.id = 333
145
146And cMediaItem.contentItemId = cItem.id
147
148and cMediaItem.mediaItemId = mItem.id
149
150And mItem.fileId = mFile.id
151 */
152
153
154
155 //create gson object and have the file write the opening bracket for valid json
156 Gson myGSon = new Gson();
157 file.write("[");
158
159 while(myResult.next()){
160 //string/object as key/value pair
161 HashMap<String, Object> dbRow = new HashMap<String, Object>(dbColumn);
162
163 for(int i = 1; i <= dbColumn; i++){
164 /*
165 doing checking this way could mean we have to have a case
166 for every data type,
167 if a data type changes in the database that's probably fine, but
168 if a new data type is sent in, we'll have update the statements
169 */
170 /*
171 Without this kind of check though, the output would throw a pretty nasty
172 warning about illegal reflective access. Though it would still print the
173 date out fine. The warning:
174
175 WARNING: An illegal reflective access operation has occurred
176 WARNING: Illegal reflective access by com.google.gson.internal.bind.ReflectiveTypeAdapterFactory (file:/home/su41/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.7/751f548c85fa49f330cecbb1875893f971b33c4e/gson-2.7.jar) to field java.time.LocalDateTime.date
177 WARNING: Please consider reporting this to the maintainers of com.google.gson.internal.bind.ReflectiveTypeAdapterFactory
178 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
179 WARNING: All illegal access operations will be denied in a future release
180 */
181 if(resultMetaData.getColumnTypeName(i).equals("DATETIME")){
182 dbRow.put(resultMetaData.getColumnName(i), myResult.getDate(i));
183 }
184 else{
185 dbRow.put(resultMetaData.getColumnName(i), myResult.getObject(i));
186 }
187 }
188 String dbRowAsJson = myGSon.toJson(dbRow);
189
190
191 //saving to json file
192 file.write(dbRowAsJson);
193
194 //Adding commas between each row that we write to our file, as we need it to be valid json
195 if(!myResult.isLast()){
196 file.write(",");
197 }
198 }
199 //write the closing bracket, and flush the writer
200 file.write("]");
201 file.flush();
202 file.close();
203 //System.out.println(myList);
204 System.out.println("Closing connection...");
205 myResult.close();
206 stmt.close();
207 conn.close();
208 System.out.println("Connection closed");
209 }
210 catch(SQLException sqlError){
211 // looking at the sql error code is good for debugging.
212 // can ref: https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-error-sqlstates.html
213 // to see what they're for
214
215 // System.out.println("MySQL error code: " + sqlError.getErrorCode());
216
217
218 //prints out the error message from sql, then check to see which error code
219 // came back and what the code matches.
220 // if it's access denied, usually something to do with username, password or db name
221 System.out.println(sqlError.getMessage());
222
223 if(sqlError.getErrorCode() == MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR){
224 System.out.println("Error with database credentials. Double check your: ");
225 System.out.println(" -'Username'");
226 System.out.println(" -'Password' ");
227 }
228
229 if(sqlError.getErrorCode() == MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR){
230 System.out.println("Error with database credentials. Double check your: ");
231 System.out.println(" -'Database name' ");
232 }
233 returnStatus = 1;
234
235 }
236 catch(Exception e){
237 //catch for class.name
238 e.printStackTrace();
239 //print out wrong username or password instead of the stack trace?
240 System.out.println("encountered error");
241 returnStatus = 2;
242 }
243 return returnStatus;
244 }
245}
Note: See TracBrowser for help on using the repository browser.