{"id":1814,"date":"2021-07-21T14:34:42","date_gmt":"2021-07-21T14:34:42","guid":{"rendered":"https:\/\/smartfoxserver.com\/blog\/?p=1814"},"modified":"2021-09-10T08:19:43","modified_gmt":"2021-09-10T08:19:43","slug":"using-a-database-in-overcast-part-2","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-2\/","title":{"rendered":"Using a database in Overcast (part 2)"},"content":{"rendered":"\n<p>In the <a rel=\"noreferrer noopener\" aria-label=\"previous installment (opens in a new tab)\" href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-1\/\" target=\"_blank\">previous installment<\/a> of the this tutorial we have learned all the steps to create a database server in Overcast and connect to it from an existing SFS2X instance.<\/p>\n\n\n\n<p>In this new chapter we are going to explore the database API that can be used to query the database from server side, using SFS2X Extensions.<\/p>\n\n\n\n<p>If you are new to server side coding we highly recommend to get started with <a rel=\"noreferrer noopener\" aria-label=\"starting from our documentation (opens in a new tab)\" href=\"http:\/\/docs2x.smartfoxserver.com\/ExtensionsJava\/quick-start\" target=\"_blank\">this article from our documentation<\/a>, before proceeding with the rest of the tutorial.  <\/p>\n\n\n\n<!--more-->\n\n\n\n<h3>\u00bb API Choices<\/h3>\n\n\n\n<p>Essentially all database access in SFS2X is done via the standard <a rel=\"noreferrer noopener\" aria-label=\"Java JDBC API (opens in a new tab)\" href=\"https:\/\/docs.oracle.com\/javase\/8\/docs\/technotes\/guides\/jdbc\/\" target=\"_blank\">Java JDBC API<\/a>, which provides an abstraction for talking to all kinds of relational databases. <\/p>\n\n\n\n<p>In addition to <strong>JDBC<\/strong> we provide a simplified layer on top of it, called the <strong>DBManager<\/strong> <strong>API<\/strong>,<strong> <\/strong>that offers a more streamlined approach to querying and manipulating records.<\/p>\n\n\n\n<p>If you&#8217;re new to JDBC we highly recommend to start with the latter and eventually move to JDBC, if necessary. <\/p>\n\n\n\n<h3>\u00bb Database Manager API Examples<\/h3>\n\n\n\n<p>Before looking at the code we assume, as a prerequisite, that you&#8217;ve already setup your database and configured SFS2X. If not, make sure to go back to <a href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-1\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"part one (opens in a new tab)\">part one<\/a> of this article and follow the steps there.<\/p>\n\n\n\n<p>We&#8217;ll start off with a basic query example:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\npublic class OvercastDBExtension extends SFSExtension\n{\n\tprivate IDBManager dbMan;\n\t\n\t@Override\n\tpublic void init()\n\t{\n\t\tdbMan = getParentZone().getDBManager();\n\t\tString sql = &quot;SELECT username FROM highscores&quot;;\n\t\ttrace(&quot;About to execute: &quot; + sql);\n\t\t\n\t\ttry\n\t\t{\n\t\t\tISFSArray result = dbMan.executeQuery(sql);\n\t\t\ttrace(result.getDump());\n\t\t}\n\t\tcatch(SQLException ex)\n\t\t{\n\t\t\ttrace(&quot;Error executing query: &quot; + ex.getMessage());\n\t\t}\n\t}\n}\n<\/pre>\n\n\n\n<p>In the <strong>init()<\/strong> method we get a reference to the <strong>DBManager<\/strong> object associated with the current zone. This is the entity that manages our connections and allows us to execute queries, inserts, updates and so on.<\/p>\n\n\n\n<p>Next we execute a basic SQL statement to extract all the user names from the table created in part one of this article. It is important to note that the execution is wrapped in a <strong>try\/catch<\/strong> block to catch any potential SQLException, which can occur if the database is offline or there&#8217;s an error in the SQL statement etc.<\/p>\n\n\n\n<p>The result of the operation is an <strong>SFSArray<\/strong> containing as many <strong>SFSObject<\/strong> instances are there are results. In other words the returned array is a <strong>collection of records<\/strong>, each containing <strong>all of the columns that we have selected<\/strong> (in this case only one).<\/p>\n\n\n\n<p>The last line:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\ntrace(result.getDump());\n<\/pre>\n\n\n\n<p>dumps the content of the SFSArray in the standard output, which will look like this:<\/p>\n\n\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n(sfs_object) \n\t(utf_string) username: fozzie\n\t\t\n(sfs_object) \n\t(utf_string) username: gonzo\n\t\t\n (sfs_object) \n\t(utf_string) username: kermit\n<\/pre>\n\n\n\n<p>Here we find listed every SFSObject contained in the array and its key-value content. Because we only selected the <strong>username<\/strong> field in our SQL, we only get one key-value pair in each SFSObject. <\/p>\n\n\n\n<h3>\u00bb Sanitizing external input<\/h3>\n\n\n\n<p>In the code above we have a simple SQL query with no parameters coming from outside but in a more realistic use case we would likely have several parameters coming from the client, which we cannot trust.<\/p>\n\n\n\n<p>To avoid any <a rel=\"noreferrer noopener\" aria-label=\"SQL injection shenanigans (opens in a new tab)\" href=\"https:\/\/en.wikipedia.org\/wiki\/SQL_injection\" target=\"_blank\">SQL injection shenanigans<\/a> we&#8217;ll use an overloaded version of the executeQuery method available it the DBManager API: <strong>executeQuery(String, Object[])<\/strong> which takes a second argument with the parameters we want to use.<\/p>\n\n\n\n<p>An example will clarify how this works:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n\npublic int findHighscore(String userName)\n{\n\tdbMan = getParentZone().getDBManager();\n\tString sql = &quot;SELECT * FROM highscores WHERE username=?&quot;;\n\tint highscore = -1;\n\n\ttry\n\t{\n\t\tISFSArray result = dbMan.executeQuery(sql, new Object[]{ userName });\n\t\t\n\t\tif (result.size() &amp;amp;amp;gt; 0)\n\t\t{\n\t\t\thighscore = result.getSFSObject(0).getInt(&quot;highscore&quot;);\n\t\t}\n\t}\n\tcatch(SQLException ex)\n\t{\n\t\ttrace(&quot;Error executing query: &quot; + ex.getMessage());\n\t}\n\n\treturn highscore;\n}\n<\/pre>\n\n\n\n<p>Here we have slightly modified version of the previous code: we have created a method that searches for a username and returns his\/her high score, if it exists.<\/p>\n\n\n\n<p>Since we now have an external parameter (the user name) we need to sanitize whatever data has been sent by the client: you can notice that our SQL expression now contains a <strong>WHERE<\/strong> clause and its value is not specified. Instead it&#8217;s replaced by a placeholder (the question mark) and the actual value is passed separately in the 2nd argument of <strong>executeQuery()<\/strong>.<\/p>\n\n\n\n<p>If we needed to include more parameters in the SQL we could just add more &#8220;?&#8221; placeholders and provide the relative value in the <strong>Object[] array<\/strong>, making sure to keep the correct order.<\/p>\n\n\n\n<p>For instance:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\nString sql = &quot;SELECT * FROM highscores WHERE username=? AND highscore &amp;amp;amp;gt; ?&quot;;\n\nISFSArray result = dbMan.executeQuery(sql, new Object[]{ userName, minScore });\n<\/pre>\n\n\n\n<h3>\u00bb Inserting, updating and deleting records<\/h3>\n\n\n\n<p>It won&#8217;t come as a surprise that inserting and updating records follows a very similar approach, by calling a different method of the <strong>DBManager API<\/strong>.<\/p>\n\n\n\n<p>Let&#8217;s take a look at how to insert a new record first:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n\tpublic void testInsert(String username, int score)\n\t{\n\t\tdbMan = getParentZone().getDBManager();\n\t\tString sql = &quot;INSERT INTO highscores (username, score) VALUES (?, ?)&quot;;\n\t\t\n\t\ttry\n\t\t{\n\t\t\tdbMan.executeInsert(sql, new Object[] { username, score });\n\t\t\ttrace(&quot;DB Insert success&quot;);\n\t\t} \n\t\t\n\t\tcatch (SQLException ex)\n\t\t{\n\t\t\ttrace(&quot;DB Insert failure: &quot; + ex.getMessage());\n\t\t}\n\t}\n<\/pre>\n\n\n\n<p>which can be invoked like this:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\ntestInsert(&quot;swedish cook&quot;, 1300);\n<\/pre>\n\n\n\n<p>You&#8217;ll notice that the example code is almost identical to the previous query snippets with the difference that we are calling <strong>executeInsert()<\/strong>, rather than <strong>executeQuery()<\/strong>.<\/p>\n\n\n\n<h4>\u00bb Record update<\/h4>\n\n\n\n<p>Now let&#8217;s see how the update works:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n\tpublic void testUpdate(String username, int score)\n\t{\n\t\tdbMan = getParentZone().getDBManager();\n\t\tString sql = &quot;UPDATE highscores SET score=? WHERE username=?&quot;;\n\t\t\n\t\ttry\n\t\t{\n\t\t\tdbMan.executeInsert(sql, new Object[] { score, username });\n\t\t\ttrace(&quot;DB Update success&quot;);\n\t\t} \n\t\t\n\t\tcatch (SQLException ex)\n\t\t{\n\t\t\ttrace(&quot;DB Update failure: &quot; + ex.getMessage());\n\t\t}\n\t}\n<\/pre>\n\n\n\n<p>And we can invoke the method like this:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">testUpdate(&quot;swedish cook&quot;, 1500)<\/pre>\n\n\n\n<h4>\u00bb Record deletion<\/h4>\n\n\n\n<p>Finally let&#8217;s take a look at deleting a record from an existing table:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\n\tpublic void testDelete(String username)\n\t{\n\t\tdbMan = getParentZone().getDBManager();\n\t\tString sql = &quot;DELETE FROM highscores WHERE username=?&quot;;\n\t\t\n\t\ttry\n\t\t{\n\t\t\tdbMan.executeInsert(sql, new Object[] { username });\n\t\t\ttrace(&quot;DB Delete success&quot;);\n\t\t} \n\t\t\n\t\tcatch (SQLException ex)\n\t\t{\n\t\t\ttrace(&quot;DB Delete failure: &quot; + ex.getMessage());\n\t\t}\n\t}\n<\/pre>\n\n\n\n<p>And let&#8217;s invoke it:<\/p>\n\n\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">testDelete(&quot;swedish cook&quot;)<\/pre>\n\n\n\n<h3>\u00bb Next steps<\/h3>\n\n\n\n<p>Combining the tutorials from <strong><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><\/strong> and part two of this series you should be able to get cracking with SFS2X and server-side databases coding.<\/p>\n\n\n\n<p>In the next (and last) episode we will take look at more advanced ways of working with databases using the standard <strong>Java JDBC API<\/strong> and provide some tips on local and remote deployment and testing. (<a href=\"https:\/\/smartfoxserver.com\/blog\/using-a-database-in-overcast-part-3\/\">Go to part 3<\/a>)<\/p>\n\n\n\n<p>If you have any comment or questions let us know via our <a href=\"https:\/\/www.smartfoxserver.com\/forums\/viewforum.php?f=18\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"SmartFoxServer support forum (opens in a new tab)\">SmartFoxServer support forum<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous installment of the this tutorial we have learned all the steps to create a database server in Overcast and connect to it from an existing SFS2X instance. In this new chapter we are going to explore the database API that can be used to query the database from server side, using SFS2X [&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,135,132,137,7],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1814"}],"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=1814"}],"version-history":[{"count":20,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1814\/revisions"}],"predecessor-version":[{"id":1971,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/1814\/revisions\/1971"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=1814"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=1814"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=1814"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}