diff --git a/README.md b/README.md index de45346..0c6f634 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,5 @@ This file contains the configuration for `Hugo`, `asciidoctor` and `sebi-theme`. ### Package.json -This file contains the needed packages to be able to build the website. \ No newline at end of file +This file contains the needed packages to be able to build the website. + diff --git a/topics/code/simplejdbc/src/main/java/simplejdbc/PgJDBCUtils.java b/topics/code/simplejdbc/src/main/java/simplejdbc/PgJDBCUtils.java index 16bf224..a643311 100644 --- a/topics/code/simplejdbc/src/main/java/simplejdbc/PgJDBCUtils.java +++ b/topics/code/simplejdbc/src/main/java/simplejdbc/PgJDBCUtils.java @@ -11,7 +11,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; -import java.util.logging.Level; import java.util.logging.Logger; import javax.sql.DataSource; import org.postgresql.ds.PGSimpleDataSource; diff --git a/topics/code/simplejdbc/src/main/java/simplejdbc/StudentDemo.java b/topics/code/simplejdbc/src/main/java/simplejdbc/StudentDemo.java index 327efef..e3cbaf0 100644 --- a/topics/code/simplejdbc/src/main/java/simplejdbc/StudentDemo.java +++ b/topics/code/simplejdbc/src/main/java/simplejdbc/StudentDemo.java @@ -33,8 +33,7 @@ public StudentDemo(DataSource ds, PrintStream out) { public List listAll() { var result = new ArrayList(); - try ( Connection con = ds.getConnection(); - PreparedStatement pst = con.prepareStatement( query ); + try ( Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement( query ); ResultSet rs = pst.executeQuery(); ) { while ( rs.next() ) { Integer snummer = rs.getInt( 1 ); @@ -47,7 +46,7 @@ public List listAll() { String student_class = rs.getString( 8 ); Boolean active = rs.getBoolean( 9 ); - Student student = new Student( snummer, firstname, lastname, + Student student = new Student( snummer, firstname, lastname, dob, cohort, email, gender, student_class, active ); result.add( student ); } @@ -57,11 +56,10 @@ public List listAll() { return result; } - private static final String insertSql = - """ - insert into students (student_id,firstname,lastname,dob,cohort,email,gender,student_grp,active) - values( ?, ?, ?, ?, ?, ?, ?, ?, ?) - """; + private static final String insertSql = """ + insert into students (student_id,firstname,lastname,dob,cohort,email,gender,student_grp,active) + values( ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; public void insertStudents(List students) { try ( Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement( insertSql ); ) { @@ -100,7 +98,7 @@ public static void main(String[] args) throws IOException { myClass.stream().forEach( System.out::println ); - if ( myClass.size() == 0 ) { + if ( myClass.isEmpty() ) { System.out.println( "Mh, none found. Where are they??" ); System.out.println( "new balls please" ); diff --git a/topics/jdbc.adoc b/topics/jdbc.adoc index 9f7a5fa..54db42e 100644 --- a/topics/jdbc.adoc +++ b/topics/jdbc.adoc @@ -12,6 +12,13 @@ to configure access to a resource, and also by the programming language. + In java the tradition is to use so called properties files, which, also traditionally, have a file extension `.properties`. It also helps to give such files well known names, so the program can refer to them by that name. +[WARNING] +==== +Putting secrets like credentials in a properties file is not secure. It is just a way to separate configuration from code. +You need to exclude the actual secrets from the code and from the project files that are under version control. The best way is +to actually not put them in the project at all, but to use a secret manager or a vault. For now we will stick to the properties file and put it in the current dierctory, that is the directory from which you run the program. Another good place would be the user home directory or a child directory of that. For instance the same dierectory that stores the maven stuff, the ${user.home}.m2/directory. +==== + For the demonstrations in this part we will use the following properties file. .[black]#db.properties# file specifying connection details for dev and prod @@ -25,6 +32,9 @@ You can see that the properties file supports two environments, **dev**elopment .Configuring a datasource. Put it in a utility class. [source,java] ---- + +// read properties +======= public class DBProvider { static Map cache = new HashMap<>(); @@ -55,21 +65,21 @@ public class DBProvider { ); } + /** + * Reads properties from a file. The properties file is taken a well known location that is known by all developers. + * for instance {"user.home"}/.m2/db.properties. + */ static Properties getProperties(String propertiesFileName){ - - System.out.println("The user working dir is " + System.getProperty("user.dir")); - - // Usage of resource file is preferred way. No issues with working dir. - // Uses the default location of resources (in src/main/java/resources dir) - // getClassLoader() is necessary, unless you store your proprty file in a - // subfolder according to package name - // (src/main/resources/fontys/customerdbdemo in this case). - Properties properties = new Properties(); - try (InputStream dbProperties = DBProvider.class.getClassLoader().getResourceAsStream(propertiesFileName);) { - properties.load(dbProperties); - } catch (IOException ex) { - Logger.getLogger(DBProvider.class.getName()).log(Level.SEVERE, null, ex); + File propFile= Path.of(System.getProperty("user.home").resolve(".m2").resolve(propertiesFileName)).toFile(); + try ( + FileInputStream fis = new FileInputStream( propFile ); ) { + properties.load( fis ); + } catch ( IOException ignored ) { + Logger.getLogger( DBProvider.class.getName() ).log( + Level.INFO, + "attempt to read file from well known location failed'", + ignored ); } return properties; } @@ -180,7 +190,7 @@ returning * -- <3> <1> Fields can be supplied in any order, including definition order. <2> You do not need the spaces before the commas, we added them for readability. <3> It is smart to *always* expect back what has been inserted by the database, including generated id and - other fields such as the database's notion of date or time. Even smarter, but a little more work //' + other fields such as the databases notion of date or time. Even smarter, but a little more work is to specify the columns instead of a `*`, so that the order in which you receive them is independent of database schema organization and _stable_. @@ -236,8 +246,7 @@ in an array and the rest can be packed into utility methods. The holy grail is to find a way to do all kind of queries against tables, and the only thing you need to know is the table name and what entities as Java objects can be expected to -be read from or written to the table. However, start simple first! At some point you'll find duplicated code and you'll find a way to optimize your code. Typically, you'll use Generics, Lambda's and streams and a bit of reflection at some point. We think it's good to first face the issue of code that is difficult to maintain and afterwards find a solution for that, instead of providing you with a very generic and maybe complex solution without knowing which underlying problems it solves. //' - +be read from or written to the table. However, start simple first! At some point you'll find duplicated code and you'll find a way to optimize your code. Typically, you will use Generics, Lambdas and streams and a bit of reflection at some point. We think it is good to first face the issue of code that is difficult to maintain and afterwards find a solution for that, instead of providing you with a very generic and maybe complex solution without knowing which underlying problems it solves. // == Traditional preparing statements diff --git a/topics/logging.adoc b/topics/logging.adoc index 342115a..18072ce 100644 --- a/topics/logging.adoc +++ b/topics/logging.adoc @@ -17,7 +17,9 @@ public static void main( String[] args ) { <1> Create a specific logger for this class. <2> This log statement only does 'work' when the log level is set to FINE or higher (FINER, FINEST) <3> Note the use of positional parameters in the format string. It can be quite useful. See it as a free and random tip. -<4> This lambda has the shape of a supplier and is only evaluated or computed when the logging level is active. + +<4> The lambda has the shape of a supplier and is only evaluated or computed when the logging level is active. This +make logging 'cheap' when the log level is not active. === Replace `soutv` by using a log macro