-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
1,565 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[languages] | ||
js = ["prettier"] | ||
java = ["google-java-format -"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
export const javalin = [ | ||
{ text: 'Introduction', link: '/javalin/index' }, | ||
{ | ||
text: 'Data layer', | ||
items: [ | ||
{ | ||
text: 'Managing the database', | ||
link: '/javalin/managing-the-database' | ||
}, | ||
{ | ||
text: 'Connecting to a database', | ||
link: '/javalin/connecting-to-a-database' | ||
}, | ||
{ | ||
text: 'Adding a model layer', | ||
link: '/javalin/adding-a-model-layer' | ||
} | ||
] | ||
}, | ||
{ | ||
text: 'Creating an API', | ||
items: [ | ||
{ text: 'Creating a server', link: '/javalin/creating-a-server' }, | ||
{ text: 'Routing and Controllers', link: '/javalin/routing' }, | ||
{ | ||
text: 'Request and response', | ||
link: '/javalin/request-response', | ||
collapsed: true, | ||
items: [ | ||
{ text: 'Query params', link: '/javalin/query-params' }, | ||
{ text: 'URL params', link: '/javalin/url-params' }, | ||
{ text: 'Body and headers', link: '/javalin/body-and-headers' } | ||
] | ||
}, | ||
{ text: 'Sending errors', link: '/javalin/sending-errors' }, | ||
] | ||
}, | ||
{ | ||
text: 'Server side rendering', | ||
items: [ | ||
{ text: 'Static files', link: '/javalin/static-files' }, | ||
{ text: 'Views and templates', link: '/javalin/views-and-templates' }, | ||
{ text: 'Using loops', link: '/javalin/using-loops' }, | ||
{ text: 'Template partials', link: '/javalin/template-partials' }, | ||
{ text: 'CSS and assets', link: '/javalin/css-and-assets' }, | ||
{ text: 'User input', link: '/javalin/user-input' } | ||
] | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Creating a Maven project | ||
|
||
## | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
# Adding a model layer | ||
|
||
We'll need to create the _model_ layer and the _data access layer_. These are the parts of our application that deals with handling data models and persisiting data using the database. | ||
|
||
## Models and Repositories | ||
|
||
Models are classes that we use to represent data within the application. | ||
|
||
```java | ||
package com.corndel.bleeter.models; | ||
|
||
public class User { | ||
private Integer id; | ||
public String username; | ||
public boolean verified; | ||
|
||
public User(Integer id, String username, boolean verified) { | ||
this.id = id; | ||
this.username = username; | ||
this.verified = verified; | ||
} | ||
|
||
public Integer getId() { | ||
return id; | ||
} | ||
} | ||
``` | ||
|
||
Repositories are classes that interact with the database to let us persist, modify, and delete this data. | ||
|
||
|
||
```java | ||
package com.corndel.bleeter.repositories; | ||
|
||
import com.corndel.bleeter.models.User; | ||
import com.corndel.bleeter.DB; | ||
import java.sql.SQLException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class UserRepository { | ||
public static List<User> findAll() throws SQLException { | ||
var query = "SELECT id, username, verified FROM users"; | ||
|
||
try (var connection = DB.getConnection(); | ||
var statement = connection.createStatement(); | ||
var resultSet = statement.executeQuery(query);) { | ||
|
||
var users = new ArrayList<User>(); | ||
while (resultSet.next()) { | ||
var id = resultSet.getInt("id"); | ||
var username = resultSet.getString("username"); | ||
var verified = resultSet.getBoolean("verified"); | ||
users.add(new User(id, username, verified)); | ||
} | ||
return users; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
## Querying with substitutions | ||
|
||
JDBC lets us set up _Prepared Statements_. These let us substitute in parameters to our SQL queries. | ||
|
||
```java | ||
public static User findById(id) { | ||
var query = "SELECT id, username, verified FROM users WHERE id = ?"; // [!code highlight] | ||
try (var connection = DB.getConnection(); | ||
var statement = connection.prepareStatement(query)) { // [!code highlight] | ||
statement.setInt(1, id) // [!code highlight] | ||
try (var resultSet = statement.executeQuery()) { | ||
if (!resultSet.next()) { | ||
return null; | ||
} | ||
var id = resultSet.getInt("id"); | ||
var username = resultSet.getString("username"); | ||
var verified = resultSet.getBoolean("verified"); | ||
return new User(id, username, verified); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
::: danger | ||
|
||
Do not be tempted to interpolate raw arguments into the query string. This opens | ||
you up to SQL injection attacks. | ||
|
||
Consider | ||
|
||
```java | ||
User.findById("3; DROP TABLE users;"); | ||
``` | ||
|
||
Always use prepared statements! | ||
|
||
::: | ||
|
||
## Inserting data | ||
|
||
We can use an `INSERT` query with several parameters by putting more `?` and | ||
passing the substitutions in with `.setString()`, `.setInt()`, or the appropriate set method for the datatype: | ||
|
||
```java | ||
public static User create(username, verified) { | ||
var query = // [!code highlight:2] | ||
"INSERT INTO users (username, verified) VALUES (?, ?) RETURNING *"; | ||
|
||
try (var connection = DB.getConnection(); | ||
var statement = con.prepareStatement(query)) { | ||
statement.setString(1, username); // [!code highlight] | ||
statement.executeUpdate(); // [!code highlight] | ||
|
||
try (var resultSet = statement.getResultSet()) { | ||
rs.next() | ||
var id = rs.getInt("id"); | ||
return new User(id, username, verified) | ||
} | ||
} | ||
} | ||
``` | ||
|
||
::: info | ||
|
||
Note the `RETURNING *` causes the statement to have a `resultSet` after execution. This lets us get the `id` and other fields of the newly created `User` from the database. | ||
|
||
::: |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Body and headers | ||
|
||
## Headers | ||
|
||
When we send a request, the headers are available in the backend as `ctx.header(String)`. | ||
|
||
## Endpoints with body | ||
|
||
### Accessing the body text | ||
|
||
### Form data | ||
|
||
### JSON | ||
|
||
When we send a request with a JSON body, we can use `ctx.bodyAsClass(Some.class)` to parse the JSON into an object using a class. | ||
|
||
::: code-group | ||
|
||
```java{8-10,13-18} [server] | ||
public class App { | ||
private Javalin app; | ||
public App() { | ||
app.post( | ||
"/users", | ||
ctx -> { | ||
var body = ctx.bodyAsClass(UserRequestBody.class); | ||
var newUser = UserRepository.create(body.username, body.verified); | ||
res.status(HttpStatus.CREATED).json(newUser); | ||
}); | ||
} | ||
class UserRequestBody { | ||
public String username; | ||
public boolean verified; | ||
public UserRequestBody() {} | ||
} | ||
} | ||
``` | ||
|
||
```bash{3} [client] | ||
curl -v -X POST http://localhost:8080/users \ | ||
-H "Content-Type: application/json" \ | ||
-d '{"username": "MinnieMouse", "verified": true}' | ||
> POST /users HTTP/1.1 | ||
> Host: localhost:8080 | ||
> User-Agent: curl/7.81.0 | ||
> Accept: */* | ||
> Content-Type: application/json | ||
> Content-Length: 45 | ||
< HTTP/1.1 200 OK | ||
< Content-Type: application/json; charset=utf-8 | ||
< Content-Length: 47 | ||
< Date: Thu, 22 Feb 2024 16:57:57 GMT | ||
{ | ||
"id": 21, | ||
"username": "MinnieMouse", | ||
"verified": 1 | ||
} | ||
``` | ||
|
||
::: | ||
|
||
Since the `ctx.bodyAsClass()` method uses the Jackson ObjectMapper under the hood, it needs a default constructor. It will then fill in the public fields that match betwwen the class and the JSON body. | ||
|
||
::: tip | ||
|
||
According to the | ||
[HTTP standard](https://www.rfc-editor.org/rfc/rfc9110.html#name-terminology-and-core-concep), | ||
`GET` requests cannot have a body, so we generally only use body in requests | ||
with `POST`, `PUT` and `PATCH`. | ||
|
||
::: |
Oops, something went wrong.