{"id":1888,"date":"2021-07-29T15:00:05","date_gmt":"2021-07-29T15:00:05","guid":{"rendered":"https:\/\/smartfoxserver.com\/blog\/?p=1888"},"modified":"2021-09-10T08:18:31","modified_gmt":"2021-09-10T08:18:31","slug":"using-a-database-in-overcast-part-3","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-3\/","title":{"rendered":"Using a database in Overcast (part 3)"},"content":{"rendered":"\n<p>In the third (and last) part of this series we are taking a look at using the <strong>JDBC API<\/strong> to access a database via SFS2X Extensions. While this approach takes a bit more coding compared to the <strong>DBManager API<\/strong> (which we have explored in <a rel=\"noreferrer noopener\" aria-label=\"part 2 (opens in a new tab)\" href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-2\/\" target=\"_blank\">part 2<\/a>) it also provides more sophisticated features such as working with metadata and advanced data types.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>If you are totally unfamiliar with the JDBC API, we highly recommend to take an <a rel=\"noreferrer noopener\" aria-label=\"introductory tour (opens in a new tab)\" href=\"https:\/\/docs.oracle.com\/javase\/tutorial\/jdbc\/overview\/index.html\" target=\"_blank\">introductory tour<\/a> before proceeding.<\/p>\n\n\n\n<p>Finally, in the last part of the article, we&#8217;ll briefly touch on local and remote development using Overcast and how to import\/export databases from your dev machine to your remote server(s).<\/p>\n\n\n\n<h3>\u00bb Accessing the database via JDBC directly<\/h3>\n\n\n\n<p>When we use JDBC in an SFS2X Extension we don&#8217;t need to setup the driver and connection details via code, which is typically the first step to get started. These steps are already done through the <strong>DBManager<\/strong> configuration in SFS2X, as we have seen in <a rel=\"noreferrer noopener\" aria-label=\"part one (opens in a new tab)\" href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-1\/\" target=\"_blank\">part one<\/a> of this article series.<\/p>\n\n\n\n<p>The <strong>DBManager<\/strong> class provides us with a pool of reusable connections that we can use to communicate with our database, regardless of the API in use.<\/p>\n\n\n\n<p>Here&#8217;s the first SQL query example using JDBC:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate void queryJDBC()\n{\n\tdbMan = getParentZone().getDBManager();\n\tConnection conn = null;\n\t\n\ttry\n\t{\n\t\tconn = dbMan.getConnection();\n\t\tPreparedStatement stmt = conn.prepareStatement(&quot;SELECT username FROM highscores&quot;);\n\t\tResultSet res = stmt.executeQuery();\n\t\t\n\t\twhile (res.next())\n\t\t{\n\t\t\tSystem.out.println(res.getString(&quot;username&quot;));\n\t\t}\n\t}\n\t\n\tcatch(SQLException ex)\n\t{\n\t\ttry\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while querying the DB: &quot; + ex.getMessage());\n\t\t\tif (conn != null)\n\t\t\t\tconn.close();\n\t\t}\n\t\t\n\t\tcatch(SQLException sqlex)\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while closing connection: &quot; + sqlex.getMessage());\n\t\t}\n\t}\n}\n<\/pre>\n\n\n\n<p>The main difference here is that we&#8217;re managing the life-cycle of the connection ourselves, with special care to closing it when we&#8217;ve completed our work. Failing to do so (by mistake or due to an Exception) will cause a connection leak which in turn will impact both memory and performance (i.e. memory leaks and connection starvation).<\/p>\n\n\n\n<p>The reason for this is that database connections are pooled (i.e. borrowed and returned from a pool of existing connections) to improve the performance and they must be returned at the end of each activity. The &#8220;returning&#8221; part is simply done by invoking the <strong>close() <\/strong>method on the connection itself.<\/p>\n\n\n\n<p>The recommended approach is to use a <strong>try\/catch\/finally<\/strong> block to deal with any potential errors and close the connection in the <strong>finally<\/strong> section, which is guaranteed to run at the exit of our method call.<\/p>\n\n\n\n<p>We&#8217;re not going into the details of how the <strong>PreparedStament<\/strong> and <strong>ResultSet<\/strong> classes work as it is outside of the scope of this article. If you need to refresh your memory please make sure to check the links at the end of the article.<\/p>\n\n\n\n<p>We will however take a look at another example using external parameters and how to properly sanitize them to avoid SQL injection issues.<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate void findScore(String username)\n{\n\tdbMan = getParentZone().getDBManager();\n\tConnection conn = null;\n\t\n\ttry\n\t{\n\t\tconn = dbMan.getConnection();\n\t\tPreparedStatement stmt = conn.prepareStatement(&quot;SELECT * FROM highscores WHERE username=?&quot;);\n\t\tstmt.setString(1, username);\n\n\t\tResultSet res = stmt.executeQuery();\n\t\t\n\t\twhile (res.next())\n\t\t{\n\t\t\tint score = res.getInt(&quot;score&quot;);\n\t\t\tSystem.out.println(&quot;Score: &quot; + score);\n\t\t}\n\t}\n\t\n\tcatch(SQLException ex)\n\t{\n\t\ttry\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while querying the DB: &quot; + ex.getMessage());\n\t\t\tif (conn != null)\n\t\t\t\tconn.close();\n\t\t}\n\t\t\n\t\tcatch(SQLException sqlex)\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while closing connection: &quot; + sqlex.getMessage());\n\t\t}\n\t}\n}\n<\/pre>\n\n\n\n<p>As we have seen in <a rel=\"noreferrer noopener\" aria-label=\"part two (opens in a new tab)\" href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-2\/\" target=\"_blank\">part two<\/a> we can use &#8220;?&#8221; placeholders in the SQL expression and populate them them separately. In the case of the <strong>PreparedStatement<\/strong> class we use the <strong>setString()<\/strong> method which uses indexes to specify which placeholder we want to replace (<strong>NOTE<\/strong>: parameter indexes start at 1 and not zero, as it happens for arrays and lists).<\/p>\n\n\n\n<p>It&#8217;s worth to note that the class supports a large number of data types and you&#8217;ll find methods such as <strong>setInt<\/strong>, <strong>setDouble<\/strong>, <strong>setBoolean<\/strong>, <strong>setBlob<\/strong>, etc.<\/p>\n\n\n\n<h3>\u00bb Using metadata<\/h3>\n\n\n\n<p>Metadata in a database doesn&#8217;t represent the content of the records but rather the records themselves, with all their properties, such as column names, types, length etc.<\/p>\n\n\n\n<p>This is akin to the reflection API in Java (and similar programming languages) which allows for self-inspection of the data types.<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nprivate void testMetadata()\n{\n\tdbMan = getParentZone().getDBManager();\n\tConnection conn = null;\n\t\n\ttry\n\t{\n\t\tconn = dbMan.getConnection();\n\t\tDatabaseMetaData databaseMetaData = conn.getMetaData();\n\t\t\n\t\tResultSet resultSet = databaseMetaData.getColumns(null,null, &quot;highscores&quot;, null); \n\t\t\n\t\twhile(resultSet.next()) \n\t\t{ \n\t\t\tString name = resultSet.getString(&quot;COLUMN_NAME&quot;);\n\t\t\tString type = resultSet.getString(&quot;DATA_TYPE&quot;);\n\t\t    String size = resultSet.getString(&quot;COLUMN_SIZE&quot;);\n\t\t    String isNullable = resultSet.getString(&quot;IS_NULLABLE&quot;);\n\t\t    \n\t\t    trace(&quot;Name: %s, Type:%s, Size: %s, isNullable: %s\\n&quot;, name, type, size, isNullable);\n\t\t}\n\t}\n\t\n\tcatch(SQLException ex)\n\t{\n\t\ttry\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while querying the DB: &quot; + ex.getMessage());\n\t\t\tif (conn != null)\n\t\t\t\tconn.close();\n\t\t}\n\t\t\n\t\tcatch(SQLException sqlex)\n\t\t{\n\t\t\ttrace(&quot;Unexpected error while closing connection: &quot; + sqlex.getMessage());\n\t\t}\n\t}\n}\n<\/pre>\n\n\n\n<p>The snippet above is an example of how to inspect a database table and extract column name, type, length and nullable properties. This can be useful when we need to dynamically work with tables without knowing all the details of their structure in advance.<\/p>\n\n\n\n<h3>\u00bb Testing locally and remotely<\/h3>\n\n\n\n<p>When working on an Overcast-based application with a database it can help to be able to test locally in order to build the DB tables and Extensions needed.<\/p>\n\n\n\n<p>In the Overcast cloud we use MySQL so the best choice is to mirror the same setup (SFS2X + MySQL) in your local environment, possibly using the same database version that was deployed in the cloud, or at least the same major version (you can check the Overcast DB version via the HQ server details page).<\/p>\n\n\n\n<h4>How to create a local setup<\/h4>\n\n\n\n<p>A quick way to setup a local <strong>MySQL<\/strong> and <strong>PHPMyAdmin<\/strong> (the web-based admin tool) is to use a pre-packaged LAMP stacks such as  <a rel=\"noreferrer noopener\" aria-label=\"XAMPP (opens in a new tab)\" href=\"https:\/\/www.apachefriends.org\/index.html\" target=\"_blank\">XAMPP<\/a> or <a rel=\"noreferrer noopener\" aria-label=\"MAMP (opens in a new tab)\" href=\"https:\/\/www.mamp.info\/\" target=\"_blank\">MAMP<\/a>. <\/p>\n\n\n\n<p>These tools provide all the necessary components to get started and they can be installed locally alongside SmartFoxServer 2X.<\/p>\n\n\n\n<p><strong>NOTE:<\/strong> in the case of XAMPP the database installed is <strong><a rel=\"noreferrer noopener\" aria-label=\"MariaDB (opens in a new tab)\" href=\"https:\/\/mariadb.com\/\" target=\"_blank\">MariaDB<\/a><\/strong>, rather than <strong>MySQL<\/strong>, which can be considered a drop-in replacement for MySQL. Generally speaking you should have no problems developing with it and then moving back to MySQL in the cloud. If you want to learn more about this aspect please <a rel=\"noreferrer noopener\" aria-label=\"consult their documentation here (opens in a new tab)\" href=\"https:\/\/mariadb.com\/kb\/en\/mariadb-vs-mysql-compatibility\/\" target=\"_blank\">consult their documentation here<\/a>.<\/p>\n\n\n\n<h4>How to import\/export a database<\/h4>\n\n\n\n<p>If you&#8217;re creating and developing the database tables locally you will need a way to export them to a file format and re-import them in the remote Overcast DB.<\/p>\n\n\n\n<p>This is easily achieved via the <strong>import\/export function<\/strong> in PHPMyAdmin.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"809\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-1024x809.jpg\" alt=\"\" class=\"wp-image-1929\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-1024x809.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-300x237.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-768x607.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-1536x1214.jpg 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd-624x493.jpg 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-select-example-hd.jpg 1578w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>In this case we have selected a single table, but we could have also done it with the whole database. Next we select the <strong>Export<\/strong> tab, leave the default settings (Quick Export, in SQL format) and proceed. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"694\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-1024x694.jpg\" alt=\"\" class=\"wp-image-1932\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-1024x694.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-300x203.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-768x520.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-1536x1041.jpg 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export-624x423.jpg 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/db-export.jpg 1960w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This will generate a <strong>local .sql file<\/strong> containing both the structure and data of the table we&#8217;ve just selected. <\/p>\n\n\n\n<p>Now we can move to the Overcast side, select our database server in the HQ and click the admin tool icon:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"141\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-1024x141.jpg\" alt=\"\" class=\"wp-image-1933\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-1024x141.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-300x41.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-768x106.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-1536x212.jpg 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-2048x283.jpg 2048w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/database-server-select-624x86.jpg 624w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This will bring us to the <strong>remote<\/strong> <strong>PHPMyAdmin<\/strong> where we can start by creating a database with the same name as the one used locally, in this case: <strong>overcast_tutorial<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"785\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db-1024x785.jpg\" alt=\"\" class=\"wp-image-1934\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db-1024x785.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db-300x230.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db-768x589.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db-624x479.jpg 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/create-db.jpg 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Once this is done, we move to the <strong>Import tab<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"657\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast-1024x657.jpg\" alt=\"\" class=\"wp-image-1937\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast-1024x657.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast-300x192.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast-768x493.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast-624x400.jpg 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/import-db-overcast.jpg 1395w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Here we simply specify the previously created .sql file from our local filesystem and upload it. The table(s) will be recreated remotely and we should be good to go.<\/p>\n\n\n\n<p>A quick check shows that we have indeed recreated our table and the relative data in the Overcast MySQL DB.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"797\" src=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-1024x797.jpg\" alt=\"\" class=\"wp-image-1938\" srcset=\"https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-1024x797.jpg 1024w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-300x234.jpg 300w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-768x598.jpg 768w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-1536x1196.jpg 1536w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db-624x486.jpg 624w, https:\/\/smartfoxserver.com\/blog\/wp-content\/uploads\/2021\/07\/imported-db.jpg 1724w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3>\u00bb More JDBC resources<\/h3>\n\n\n\n<p>If you want to read more about the <strong>JDBC API<\/strong> we&#8217;d like to recommend several articles and tutorials that will help you learn the ins and outs of the library.<\/p>\n\n\n\n<ul><li><a rel=\"noreferrer noopener\" aria-label=\"Baeldung JDBC Introduction (opens in a new tab)\" href=\"https:\/\/www.baeldung.com\/java-jdbc\" target=\"_blank\">Baeldung JDBC Introduction<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"Baeldung JDBC articles (opens in a new tab)\" href=\"https:\/\/www.baeldung.com\/tag\/jdbc\/\" target=\"_blank\">Baeldung JDBC articles<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"\u2028Infoworld JDBC tutorial (opens in a new tab)\" href=\"https:\/\/www.infoworld.com\/article\/3388036\/what-is-jdbc-introduction-to-java-database-connectivity.html\" target=\"_blank\">Infoworld JDBC tutorial<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"DZone JDBC Metadata tutorial (opens in a new tab)\" href=\"https:\/\/dzone.com\/articles\/jdbc-tutorial-extracting-database-metadata-via-jdb\" target=\"_blank\">DZone JDBC Metadata tutorial<\/a><\/li><\/ul>\n\n\n\n<p>&#8230;and, as usual, if you have any comment or questions let us know via our <a rel=\"noreferrer noopener\" href=\"https:\/\/www.smartfoxserver.com\/forums\/viewforum.php?f=18\" target=\"_blank\">SmartFoxServer support forum<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the third (and last) part of this series we are taking a look at using the JDBC API to access a database via SFS2X Extensions. While this approach takes a bit more coding compared to the DBManager API (which we have explored in part 2) it also provides more sophisticated features such as working [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23],"tags":[56,21,136,138,135,132,7],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1888"}],"collection":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/comments?post=1888"}],"version-history":[{"count":20,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1888\/revisions"}],"predecessor-version":[{"id":1966,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1888\/revisions\/1966"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=1888"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=1888"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=1888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}