diff --git a/README.md b/README.md
index d018783465..d20968b455 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
The "REST with Spring" Classes
==============================
-After 5 months of work, here's the Master Class of REST With Spring:
+Here's the Master Class of REST With Spring (price changes permanently next Friday):
**[>> THE REST WITH SPRING MASTER CLASS](http://www.baeldung.com/rest-with-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=rws#master-class)**
And here's the Master Class of Learn Spring Security:
diff --git a/testing-modules/streams-ordering/src/test/java/benchmarking/TestBenchmark.java b/core-java-8/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java
similarity index 97%
rename from testing-modules/streams-ordering/src/test/java/benchmarking/TestBenchmark.java
rename to core-java-8/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java
index a3e99e6465..9ae0a16514 100644
--- a/testing-modules/streams-ordering/src/test/java/benchmarking/TestBenchmark.java
+++ b/core-java-8/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java
@@ -1,4 +1,4 @@
-package benchmarking;
+package com.baeldung.streamordering;
import org.junit.Test;
import org.openjdk.jmh.annotations.*;
@@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
-public class TestBenchmark
+public class BenchmarkUnitTest
{
@Test
@@ -94,4 +94,4 @@ public class TestBenchmark
for (int i = 0; i < 1000; i++)
bh.consume (list.get (i));
}
-}
\ No newline at end of file
+}
diff --git a/testing-modules/streams-ordering/src/test/java/StreamsOrderingTest.java b/core-java-8/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java
similarity index 96%
rename from testing-modules/streams-ordering/src/test/java/StreamsOrderingTest.java
rename to core-java-8/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java
index b657911780..43a233d353 100644
--- a/testing-modules/streams-ordering/src/test/java/StreamsOrderingTest.java
+++ b/core-java-8/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java
@@ -1,3 +1,5 @@
+package com.baeldung.streamordering;
+
import org.junit.Before;
import org.junit.Test;
@@ -10,10 +12,9 @@ import java.util.stream.IntStream;
import static org.junit.Assert.assertEquals;
+public class StreamsOrderingUnitTest {
-public class StreamsOrderingTest {
-
- Logger logger = Logger.getLogger( StreamsOrderingTest.class.getName());
+ Logger logger = Logger.getLogger( StreamsOrderingUnitTest.class.getName());
@Before
public void setUp() throws Exception {
diff --git a/core-java-io/src/test/java/org/baeldung/java/io/JavaXToInputStreamUnitTest.java b/core-java-io/src/test/java/org/baeldung/java/io/JavaXToInputStreamUnitTest.java
index 08a4c673cd..6604d75ed1 100644
--- a/core-java-io/src/test/java/org/baeldung/java/io/JavaXToInputStreamUnitTest.java
+++ b/core-java-io/src/test/java/org/baeldung/java/io/JavaXToInputStreamUnitTest.java
@@ -1,10 +1,12 @@
package org.baeldung.java.io;
import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.SequenceInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -74,6 +76,28 @@ public class JavaXToInputStreamUnitTest {
IOUtils.closeQuietly(targetStream);
}
+ @Test
+ public final void givenUsingPlainJava_whenConvertingFileToDataInputStream_thenCorrect() throws IOException {
+ final File initialFile = new File("src/test/resources/sample.txt");
+ final InputStream targetStream = new DataInputStream(new FileInputStream(initialFile));
+
+ IOUtils.closeQuietly(targetStream);
+ }
+
+ @Test
+ public final void givenUsingPlainJava_whenConvertingFileToSequenceInputStream_thenCorrect() throws IOException {
+ final File initialFile = new File("src/test/resources/sample.txt");
+ final File anotherFile = new File("src/test/resources/anothersample.txt");
+ final InputStream targetStream = new FileInputStream(initialFile);
+ final InputStream anotherTargetStream = new FileInputStream(anotherFile);
+
+ InputStream sequenceTargetStream = new SequenceInputStream(targetStream, anotherTargetStream);
+
+ IOUtils.closeQuietly(targetStream);
+ IOUtils.closeQuietly(anotherTargetStream);
+ IOUtils.closeQuietly(sequenceTargetStream);
+ }
+
@Test
public final void givenUsingGuava_whenConvertingFileToInputStream_thenCorrect() throws IOException {
final File initialFile = new File("src/test/resources/sample.txt");
diff --git a/core-java-io/src/test/resources/anothersample.txt b/core-java-io/src/test/resources/anothersample.txt
new file mode 100644
index 0000000000..f06f18ce02
--- /dev/null
+++ b/core-java-io/src/test/resources/anothersample.txt
@@ -0,0 +1 @@
+...Continues
\ No newline at end of file
diff --git a/core-java/src/test/java/com/baeldung/throwsexception/PersonRepositoryUnitTest.java b/core-java/src/test/java/com/baeldung/throwsexception/PersonRepositoryUnitTest.java
index 0d1859fb1a..3749ce10d0 100644
--- a/core-java/src/test/java/com/baeldung/throwsexception/PersonRepositoryUnitTest.java
+++ b/core-java/src/test/java/com/baeldung/throwsexception/PersonRepositoryUnitTest.java
@@ -13,21 +13,31 @@ public class PersonRepositoryUnitTest {
PersonRepository personRepository = new PersonRepository();
@Test
- public void whenIdIsNull_thenExceptionIsThrown() throws Exception {
- assertThrows(Exception.class,
+ public void whenIdIsNull_thenExceptionIsThrown() {
+ assertThrows(IllegalArgumentException.class,
() ->
Optional
.ofNullable(personRepository.findNameById(null))
- .orElseThrow(Exception::new));
+ .orElseThrow(IllegalArgumentException::new));
}
@Test
- public void whenIdIsNonNull_thenNoExceptionIsThrown() throws Exception {
+ public void whenIdIsNonNull_thenNoExceptionIsThrown() {
assertAll(
() ->
Optional
.ofNullable(personRepository.findNameById("id"))
- .orElseThrow(Exception::new));
+ .orElseThrow(RuntimeException::new));
+ }
+
+ @Test
+ public void whenIdNonNull_thenReturnsNameUpperCase() {
+ String name = Optional
+ .ofNullable(personRepository.findNameById("id"))
+ .map(String::toUpperCase)
+ .orElseThrow(RuntimeException::new);
+
+ assertEquals("NAME", name);
}
@Test
diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml
index 1c5818b6aa..6fdc7c7c1a 100644
--- a/core-kotlin/pom.xml
+++ b/core-kotlin/pom.xml
@@ -1,85 +1,105 @@
- 4.0.0
- core-kotlin
- jar
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ core-kotlin
+ jar
-
- com.baeldung
- parent-kotlin
- 1.0.0-SNAPSHOT
- ../parent-kotlin
-
+
+ com.baeldung
+ parent-kotlin
+ 1.0.0-SNAPSHOT
+ ../parent-kotlin
+
-
-
- org.jetbrains.spek
- spek-api
- 1.1.5
- test
-
-
- org.jetbrains.spek
- spek-subject-extension
- 1.1.5
- test
-
-
- org.jetbrains.spek
- spek-junit-platform-engine
- 1.1.5
- test
-
-
- org.apache.commons
- commons-math3
- ${commons-math3.version}
-
-
- org.junit.platform
- junit-platform-runner
- ${junit.platform.version}
- test
-
-
- khttp
- khttp
- ${khttp.version}
-
-
- com.nhaarman
- mockito-kotlin
- ${mockito-kotlin.version}
- test
-
-
- com.github.salomonbrys.kodein
- kodein
- ${kodein.version}
-
-
- org.assertj
- assertj-core
- ${assertj.version}
- test
-
-
- com.beust
- klaxon
- ${klaxon.version}
-
-
+
+
+ exposed
+ exposed
+ https://dl.bintray.com/kotlin/exposed
+
+
-
- 1.5.0
- 4.1.0
- 3.0.4
- 0.1.0
- 3.6.1
- 1.1.1
- 5.2.0
- 3.10.0
-
+
+
+ org.jetbrains.spek
+ spek-api
+ 1.1.5
+ test
+
+
+ org.jetbrains.spek
+ spek-subject-extension
+ 1.1.5
+ test
+
+
+ org.jetbrains.spek
+ spek-junit-platform-engine
+ 1.1.5
+ test
+
+
+ org.apache.commons
+ commons-math3
+ ${commons-math3.version}
+
+
+ org.junit.platform
+ junit-platform-runner
+ ${junit.platform.version}
+ test
+
+
+ khttp
+ khttp
+ ${khttp.version}
+
+
+ com.nhaarman
+ mockito-kotlin
+ ${mockito-kotlin.version}
+ test
+
+
+ com.github.salomonbrys.kodein
+ kodein
+ ${kodein.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ com.beust
+ klaxon
+ ${klaxon.version}
+
+
+ org.jetbrains.exposed
+ exposed
+ ${exposed.version}
+
+
+ com.h2database
+ h2
+ ${h2database.version}
+
+
+
+
+ 1.5.0
+ 4.1.0
+ 3.0.4
+ 0.1.0
+ 3.6.1
+ 1.1.1
+ 5.2.0
+ 3.10.0
+ 1.4.197
+ 0.10.4
+
\ No newline at end of file
diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/exposed/ExposedTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/exposed/ExposedTest.kt
new file mode 100644
index 0000000000..29fa18ef7a
--- /dev/null
+++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/exposed/ExposedTest.kt
@@ -0,0 +1,333 @@
+package com.baeldung.kotlin.exposed
+
+import org.jetbrains.exposed.dao.*
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.transactions.TransactionManager
+import org.jetbrains.exposed.sql.transactions.transaction
+import org.junit.Test
+import java.sql.DriverManager
+import kotlin.test.*
+
+class ExposedTest {
+
+ @Test
+ fun whenH2Database_thenConnectionSuccessful() {
+ val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ transaction {
+ assertEquals(1.4.toBigDecimal(), database.version)
+ assertEquals("h2", database.vendor)
+ }
+ }
+
+ @Test
+ fun whenH2DatabaseWithCredentials_thenConnectionSuccessful() {
+ Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver", user = "myself", password = "secret")
+ }
+
+ @Test
+ fun whenH2DatabaseWithManualConnection_thenConnectionSuccessful() {
+ var connected = false
+ Database.connect({ connected = true; DriverManager.getConnection("jdbc:h2:mem:test;MODE=MySQL") })
+ assertEquals(false, connected)
+ transaction {
+ addLogger(StdOutSqlLogger)
+ assertEquals(false, connected)
+ SchemaUtils.create(Cities)
+ assertEquals(true, connected)
+ }
+ }
+
+ @Test
+ fun whenManualCommit_thenOk() {
+ Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ transaction {
+ assertTrue(this is Transaction)
+ commit()
+ commit()
+ commit()
+ }
+ }
+
+ @Test
+ fun whenInsert_thenGeneratedKeys() {
+ Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ transaction {
+ SchemaUtils.create(StarWarsFilms)
+ val id = StarWarsFilms.insertAndGetId {
+ it[name] = "The Last Jedi"
+ it[sequelId] = 8
+ it[director] = "Rian Johnson"
+ }
+ assertEquals(1, id.value)
+ val insert = StarWarsFilms.insert {
+ it[name] = "The Force Awakens"
+ it[sequelId] = 7
+ it[director] = "J.J. Abrams"
+ }
+ assertEquals(2, insert[StarWarsFilms.id]?.value)
+ val selectAll = StarWarsFilms.selectAll()
+ selectAll.forEach {
+ assertTrue { it[StarWarsFilms.sequelId] >= 7 }
+ }
+ StarWarsFilms.slice(StarWarsFilms.name, StarWarsFilms.director).selectAll()
+ .forEach {
+ assertTrue { it[StarWarsFilms.name].startsWith("The") }
+ }
+ val select = StarWarsFilms.select { (StarWarsFilms.director like "J.J.%") and (StarWarsFilms.sequelId eq 7) }
+ assertEquals(1, select.count())
+ StarWarsFilms.update ({ StarWarsFilms.sequelId eq 8 }) {
+ it[name] = "Episode VIII – The Last Jedi"
+ with(SqlExpressionBuilder) {
+ it.update(StarWarsFilms.sequelId, StarWarsFilms.sequelId + 1)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun whenForeignKey_thenAutoJoin() {
+ Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ transaction {
+ addLogger(StdOutSqlLogger)
+ SchemaUtils.create(StarWarsFilms, Players)
+ StarWarsFilms.insert {
+ it[name] = "The Last Jedi"
+ it[sequelId] = 8
+ it[director] = "Rian Johnson"
+ }
+ StarWarsFilms.insert {
+ it[name] = "The Force Awakens"
+ it[sequelId] = 7
+ it[director] = "J.J. Abrams"
+ }
+ Players.insert {
+ it[name] = "Mark Hamill"
+ it[sequelId] = 7
+ }
+ Players.insert {
+ it[name] = "Mark Hamill"
+ it[sequelId] = 8
+ }
+ val simpleInnerJoin = (StarWarsFilms innerJoin Players).selectAll()
+ assertEquals(2, simpleInnerJoin.count())
+ simpleInnerJoin.forEach {
+ assertNotNull(it[StarWarsFilms.name])
+ assertEquals(it[StarWarsFilms.sequelId], it[Players.sequelId])
+ assertEquals("Mark Hamill", it[Players.name])
+ }
+ val innerJoinWithCondition = (StarWarsFilms innerJoin Players)
+ .select { StarWarsFilms.sequelId eq Players.sequelId }
+ assertEquals(2, innerJoinWithCondition.count())
+ innerJoinWithCondition.forEach {
+ assertNotNull(it[StarWarsFilms.name])
+ assertEquals(it[StarWarsFilms.sequelId], it[Players.sequelId])
+ assertEquals("Mark Hamill", it[Players.name])
+ }
+ val complexInnerJoin = Join(StarWarsFilms, Players, joinType = JoinType.INNER, onColumn = StarWarsFilms.sequelId, otherColumn = Players.sequelId, additionalConstraint = {
+ StarWarsFilms.sequelId eq 8
+ }).selectAll()
+ assertEquals(1, complexInnerJoin.count())
+ complexInnerJoin.forEach {
+ assertNotNull(it[StarWarsFilms.name])
+ assertEquals(it[StarWarsFilms.sequelId], it[Players.sequelId])
+ assertEquals("Mark Hamill", it[Players.name])
+ }
+
+ }
+ }
+
+ @Test
+ fun whenJoinWithAlias_thenFun() {
+ Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ transaction {
+ addLogger(StdOutSqlLogger)
+ SchemaUtils.create(StarWarsFilms, Players)
+ StarWarsFilms.insert {
+ it[name] = "The Last Jedi"
+ it[sequelId] = 8
+ it[director] = "Rian Johnson"
+ }
+ StarWarsFilms.insert {
+ it[name] = "The Force Awakens"
+ it[sequelId] = 7
+ it[director] = "J.J. Abrams"
+ }
+ val sequel = StarWarsFilms.alias("sequel")
+ Join(StarWarsFilms, sequel,
+ additionalConstraint = { sequel[StarWarsFilms.sequelId] eq StarWarsFilms.sequelId + 1 })
+ .selectAll().forEach {
+ assertEquals(it[sequel[StarWarsFilms.sequelId]], it[StarWarsFilms.sequelId] + 1)
+ }
+ }
+ }
+
+ @Test
+ fun whenEntity_thenDAO() {
+ val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ val connection = database.connector.invoke() //Keep a connection open so the DB is not destroyed after the first transaction
+ val inserted = transaction {
+ addLogger(StdOutSqlLogger)
+ SchemaUtils.create(StarWarsFilms, Players)
+ val theLastJedi = StarWarsFilm.new {
+ name = "The Last Jedi"
+ sequelId = 8
+ director = "Rian Johnson"
+ }
+ assertFalse(TransactionManager.current().entityCache.inserts.isEmpty())
+ assertEquals(1, theLastJedi.id.value) //Reading this causes a flush
+ assertTrue(TransactionManager.current().entityCache.inserts.isEmpty())
+ theLastJedi
+ }
+ transaction {
+ val theLastJedi = StarWarsFilm.findById(1)
+ assertNotNull(theLastJedi)
+ assertEquals(inserted.id, theLastJedi?.id)
+ }
+ connection.close()
+ }
+
+ @Test
+ fun whenManyToOne_thenNavigation() {
+ val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ val connection = database.connector.invoke()
+ transaction {
+ addLogger(StdOutSqlLogger)
+ SchemaUtils.create(StarWarsFilms, Players, Users, UserRatings)
+ val theLastJedi = StarWarsFilm.new {
+ name = "The Last Jedi"
+ sequelId = 8
+ director = "Rian Johnson"
+ }
+ val someUser = User.new {
+ name = "Some User"
+ }
+ val rating = UserRating.new {
+ value = 9
+ user = someUser
+ film = theLastJedi
+ }
+ assertEquals(theLastJedi, rating.film)
+ assertEquals(someUser, rating.user)
+ assertEquals(rating, theLastJedi.ratings.first())
+ }
+ transaction {
+ val theLastJedi = StarWarsFilm.find { StarWarsFilms.sequelId eq 8 }.first()
+ val ratings = UserRating.find { UserRatings.film eq theLastJedi.id }
+ assertEquals(1, ratings.count())
+ val rating = ratings.first()
+ assertEquals("Some User", rating.user.name)
+ assertEquals(rating, theLastJedi.ratings.first())
+ UserRating.new {
+ value = 8
+ user = rating.user
+ film = theLastJedi
+ }
+ assertEquals(2, theLastJedi.ratings.count())
+ }
+ connection.close()
+ }
+
+ @Test
+ fun whenManyToMany_thenAssociation() {
+ val database = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
+ val connection = database.connector.invoke()
+ val film = transaction {
+ SchemaUtils.create(StarWarsFilms)
+ StarWarsFilm.new {
+ name = "The Last Jedi"
+ sequelId = 8
+ director = "Rian Johnson"
+ }
+ }
+
+ val actor = transaction {
+ SchemaUtils.create(Actors)
+ Actor.new {
+ firstname = "Daisy"
+ lastname = "Ridley"
+ }
+ }
+
+ transaction {
+ SchemaUtils.create(StarWarsFilmActors)
+ film.actors = SizedCollection(listOf(actor))
+ }
+ connection.close()
+ }
+
+}
+
+object Cities: IntIdTable() {
+ val name = varchar("name", 50)
+}
+
+object StarWarsFilms_Simple : Table() {
+ val id = integer("id").autoIncrement().primaryKey()
+ val sequelId = integer("sequel_id").uniqueIndex()
+ val name = varchar("name", 50)
+ val director = varchar("director", 50)
+}
+
+object StarWarsFilms : IntIdTable() {
+ val sequelId = integer("sequel_id").uniqueIndex()
+ val name = varchar("name", 50)
+ val director = varchar("director", 50)
+}
+
+object Players : Table() {
+ //val sequelId = integer("sequel_id").uniqueIndex().references(StarWarsFilms.sequelId)
+ val sequelId = reference("sequel_id", StarWarsFilms.sequelId).uniqueIndex()
+ //val filmId = reference("film_id", StarWarsFilms).nullable()
+ val name = varchar("name", 50)
+}
+
+class StarWarsFilm(id: EntityID) : Entity(id) {
+ companion object : EntityClass(StarWarsFilms)
+
+ var sequelId by StarWarsFilms.sequelId
+ var name by StarWarsFilms.name
+ var director by StarWarsFilms.director
+ var actors by Actor via StarWarsFilmActors
+ val ratings by UserRating referrersOn UserRatings.film
+}
+
+object Users: IntIdTable() {
+ val name = varchar("name", 50)
+}
+
+object UserRatings: IntIdTable() {
+ val value = long("value")
+ val film = reference("film", StarWarsFilms)
+ val user = reference("user", Users)
+}
+
+class User(id: EntityID): IntEntity(id) {
+ companion object : IntEntityClass(Users)
+
+ var name by Users.name
+}
+
+class UserRating(id: EntityID): IntEntity(id) {
+ companion object : IntEntityClass(UserRatings)
+
+ var value by UserRatings.value
+ var film by StarWarsFilm referencedOn UserRatings.film
+ var user by User referencedOn UserRatings.user
+}
+
+object Actors: IntIdTable() {
+ val firstname = varchar("firstname", 50)
+ val lastname = varchar("lastname", 50)
+}
+
+class Actor(id: EntityID): IntEntity(id) {
+ companion object : IntEntityClass(Actors)
+
+ var firstname by Actors.firstname
+ var lastname by Actors.lastname
+}
+
+object StarWarsFilmActors : Table() {
+ val starWarsFilm = reference("starWarsFilm", StarWarsFilms).primaryKey(0)
+ val actor = reference("actor", Actors).primaryKey(1)
+}
\ No newline at end of file
diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Address.java b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Address.java
new file mode 100644
index 0000000000..8b0a51858d
--- /dev/null
+++ b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Address.java
@@ -0,0 +1,34 @@
+package com.baeldung.hibernate.joincolumn;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class Address {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @Column(name = "ZIP")
+ private String zipCode;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+}
\ No newline at end of file
diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Email.java b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Email.java
new file mode 100644
index 0000000000..a91fb3b4c9
--- /dev/null
+++ b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Email.java
@@ -0,0 +1,47 @@
+package com.baeldung.hibernate.joincolumn;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Email {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ private String address;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "employee_id")
+ private Employee employee;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public Employee getEmployee() {
+ return employee;
+ }
+
+ public void setEmployee(Employee employee) {
+ this.employee = employee;
+ }
+}
\ No newline at end of file
diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Employee.java b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Employee.java
new file mode 100644
index 0000000000..3fbdb3820e
--- /dev/null
+++ b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Employee.java
@@ -0,0 +1,36 @@
+package com.baeldung.hibernate.joincolumn;
+
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+@Entity
+public class Employee {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @OneToMany(fetch = FetchType.LAZY, mappedBy = "employee")
+ private List emails;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public List getEmails() {
+ return emails;
+ }
+
+ public void setEmails(List emails) {
+ this.emails = emails;
+ }
+}
\ No newline at end of file
diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Office.java b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Office.java
new file mode 100644
index 0000000000..e5b9dc06bc
--- /dev/null
+++ b/hibernate5/src/main/java/com/baeldung/hibernate/joincolumn/Office.java
@@ -0,0 +1,41 @@
+package com.baeldung.hibernate.joincolumn;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinColumns;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class Office {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumns({
+ @JoinColumn(name="ADDR_ID", referencedColumnName="ID"),
+ @JoinColumn(name="ADDR_ZIP", referencedColumnName="ZIP")
+ })
+ private Address address;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
\ No newline at end of file
diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/joincolumn/JoinColumnIntegrationTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/joincolumn/JoinColumnIntegrationTest.java
new file mode 100644
index 0000000000..8246a2b01e
--- /dev/null
+++ b/hibernate5/src/test/java/com/baeldung/hibernate/joincolumn/JoinColumnIntegrationTest.java
@@ -0,0 +1,57 @@
+package com.baeldung.hibernate.joincolumn;
+
+import com.baeldung.hibernate.HibernateUtil;
+import java.io.IOException;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class JoinColumnIntegrationTest {
+
+ private Session session;
+ private Transaction transaction;
+
+ @Before
+ public void setUp() throws IOException {
+ session = HibernateUtil.getSessionFactory("hibernate-spatial.properties")
+ .openSession();
+ transaction = session.beginTransaction();
+ }
+
+ @After
+ public void tearDown() {
+ transaction.rollback();
+ session.close();
+ }
+
+ @Test
+ public void givenOfficeEntity_setAddress_shouldPersist() {
+ Office office = new Office();
+
+ Address address = new Address();
+ address.setZipCode("11-111");
+ office.setAddress(address);
+
+ session.save(office);
+ session.flush();
+ session.clear();
+ }
+
+ @Test
+ public void givenEmployeeEntity_setEmails_shouldPersist() {
+ Employee employee = new Employee();
+
+ Email email = new Email();
+ email.setAddress("example@email.com");
+ email.setEmployee(employee);
+
+ session.save(employee);
+ session.flush();
+ session.clear();
+ }
+
+}
\ No newline at end of file
diff --git a/java-streams/pom.xml b/java-streams/pom.xml
index 351c33ecc0..75914f0247 100644
--- a/java-streams/pom.xml
+++ b/java-streams/pom.xml
@@ -15,6 +15,18 @@
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ provided
+
org.apache.commons
commons-lang3
@@ -107,6 +119,7 @@
+ 1.21
3.5
1.16.12
0.9.0
diff --git a/java-streams/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java b/java-streams/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java
new file mode 100644
index 0000000000..9ae0a16514
--- /dev/null
+++ b/java-streams/src/test/java/com/baeldung/streamordering/BenchmarkUnitTest.java
@@ -0,0 +1,97 @@
+package com.baeldung.streamordering;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+
+public class BenchmarkUnitTest
+{
+
+ @Test
+ public void
+ launchBenchmark() throws Exception {
+
+ Options opt = new OptionsBuilder()
+ // Specify which benchmarks to run.
+ // You can be more specific if you'd like to run only one benchmark per test.
+ .include(this.getClass().getName() + ".*")
+ // Set the following options as needed
+ .mode (Mode.AverageTime)
+ .timeUnit(TimeUnit.MICROSECONDS)
+ .warmupTime(TimeValue.seconds(1))
+ .warmupIterations(2)
+ .measurementTime(TimeValue.seconds(1))
+ .measurementIterations(2)
+ .threads(2)
+ .forks(1)
+ .shouldFailOnError(true)
+ .shouldDoGC(true)
+ //.jvmArgs("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining")
+ //.addProfiler(WinPerfAsmProfiler.class)
+ .build();
+
+ new Runner(opt).run();
+
+
+ }
+
+ @Benchmark
+ public void givenOrderedStreamInput_whenStreamFiltered_showOpsPerMS(){
+ IntStream.range(1, 100_000_000).parallel().filter(i -> i % 10 == 0).toArray();
+ }
+
+ @Benchmark
+ public void givenUnorderedStreamInput_whenStreamFiltered_showOpsPerMS(){
+ IntStream.range(1,100_000_000).unordered().parallel().filter(i -> i % 10 == 0).toArray();
+ }
+
+ @Benchmark
+ public void givenUnorderedStreamInput_whenStreamDistinct_showOpsPerMS(){
+ IntStream.range(1, 1_000_000).unordered().parallel().distinct().toArray();
+ }
+
+ @Benchmark
+ public void givenOrderedStreamInput_whenStreamDistinct_showOpsPerMS() {
+ //section 5.1.
+ IntStream.range(1, 1_000_000).parallel().distinct().toArray();
+ }
+
+
+ // The JMH samples are the best documentation for how to use it
+ // http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/
+ @State(Scope.Thread)
+ public static class BenchmarkState
+ {
+ List list;
+
+ @Setup(Level.Trial) public void
+ initialize() {
+
+ Random rand = new Random();
+
+ list = new ArrayList<>();
+ for (int i = 0; i < 1000; i++)
+ list.add (rand.nextInt());
+ }
+ }
+
+ @Benchmark public void
+ benchmark1 (BenchmarkState state, Blackhole bh) {
+
+ List list = state.list;
+
+ for (int i = 0; i < 1000; i++)
+ bh.consume (list.get (i));
+ }
+}
diff --git a/java-streams/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java b/java-streams/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java
new file mode 100644
index 0000000000..43a233d353
--- /dev/null
+++ b/java-streams/src/test/java/com/baeldung/streamordering/StreamsOrderingUnitTest.java
@@ -0,0 +1,149 @@
+package com.baeldung.streamordering;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.junit.Assert.assertEquals;
+
+public class StreamsOrderingUnitTest {
+
+ Logger logger = Logger.getLogger( StreamsOrderingUnitTest.class.getName());
+
+ @Before
+ public void setUp() throws Exception {
+ logger.setLevel(Level.ALL);
+ }
+
+ @Test
+ public void givenTwoCollections_whenStreamed_thenCheckOutputDifferent(){
+
+ List list = Arrays.asList("B", "A", "C", "D", "F");
+ Set set = new TreeSet<>(Arrays.asList("B", "A", "C", "D", "F"));
+
+ Object[] listOutput = list.stream().toArray();
+ Object[] setOutput = set.stream().toArray();
+
+ assertEquals("[B, A, C, D, F]", Arrays.toString(listOutput));
+ assertEquals("[A, B, C, D, F]", Arrays.toString(setOutput));
+
+ }
+
+ @Test
+ public void givenTwoCollections_whenStreamedInParallel_thenCheckOutputDifferent(){
+
+ List list = Arrays.asList("B", "A", "C", "D", "F");
+ Set set = new TreeSet<>(Arrays.asList("B", "A", "C", "D", "F"));
+
+ Object[] listOutput = list.stream().parallel().toArray();
+ Object[] setOutput = set.stream().parallel().toArray();
+
+ assertEquals("[B, A, C, D, F]", Arrays.toString(listOutput));
+ assertEquals("[A, B, C, D, F]", Arrays.toString(setOutput));
+
+ }
+
+
+
+ @Test
+ public void givenOrderedInput_whenUnorderedAndOrderedCompared_thenCheckUnorderedOutputChanges(){
+ Set set = new TreeSet<>(
+ Arrays.asList(-9, -5, -4, -2, 1, 2, 4, 5, 7, 9, 12, 13, 16, 29, 23, 34, 57, 68, 90, 102, 230));
+
+ Object[] orderedArray = set.stream()
+ .parallel()
+ .limit(5)
+ .toArray();
+ Object[] unorderedArray = set.stream()
+ .unordered()
+ .parallel()
+ .limit(5)
+ .toArray();
+
+ logger.info(Arrays.toString(orderedArray));
+ logger.info(Arrays.toString(unorderedArray));
+ }
+
+
+ @Test
+ public void givenUnsortedStreamInput_whenStreamSorted_thenCheckOrderChanged(){
+
+ List list = Arrays.asList(-3,10,-4,1,3);
+
+ Object[] listOutput = list.stream().toArray();
+ Object[] listOutputSorted = list.stream().sorted().toArray();
+
+ assertEquals("[-3, 10, -4, 1, 3]", Arrays.toString(listOutput));
+ assertEquals("[-4, -3, 1, 3, 10]", Arrays.toString(listOutputSorted));
+
+ }
+
+ @Test
+ public void givenUnsortedStreamInput_whenStreamDistinct_thenShowTimeTaken(){
+ long start, end;
+ start = System.currentTimeMillis();
+ IntStream.range(1,1_000_000).unordered().parallel().distinct().toArray();
+ end = System.currentTimeMillis();
+ System.out.println(String.format("Time taken when unordered: %d ms", (end - start)));
+ }
+
+
+ @Test
+ public void givenSameCollection_whenStreamTerminated_thenCheckEachVsEachOrdered(){
+
+ List list = Arrays.asList("B", "A", "C", "D", "F");
+
+ list.stream().parallel().forEach(e -> logger.log(Level.INFO, e));
+ list.stream().parallel().forEachOrdered(e -> logger.log(Level.INFO, e));
+
+ }
+
+ @Test
+ public void givenSameCollection_whenStreamCollected_thenCheckOutput(){
+
+ List list = Arrays.asList("B", "A", "C", "D", "F");
+
+ List collectionList = list.stream().parallel().collect(Collectors.toList());
+ Set collectionSet = list.stream().parallel().collect(Collectors.toCollection(TreeSet::new));
+
+ assertEquals("[B, A, C, D, F]", collectionList.toString());
+ assertEquals("[A, B, C, D, F]", collectionSet.toString());
+
+ }
+
+
+ @Test
+ public void givenListIterationOrder_whenStreamCollectedToMap_thenCeckOrderChanged() {
+ List list = Arrays.asList("A", "BB", "CCC");
+
+ Map hashMap = list.stream().collect(Collectors.toMap(Function.identity(), String::length));
+
+ Object[] keySet = hashMap.keySet().toArray();
+
+ assertEquals("[BB, A, CCC]", Arrays.toString(keySet));
+
+ }
+
+ @Test
+ public void givenListIteration_whenStreamCollectedtoHashMap_thenCheckOrderMaintained() {
+ List list = Arrays.asList("A", "BB", "CCC", "CCC");
+
+ Map linkedHashMap = list.stream().collect(Collectors.toMap(
+ Function.identity(),
+ String::length,
+ (u, v) -> u,
+ LinkedHashMap::new
+ ));
+
+ Object[] keySet = linkedHashMap.keySet().toArray();
+
+ assertEquals("[A, BB, CCC]", Arrays.toString(keySet));
+ }
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/dao/BookRepositoryImpl.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/dao/BookRepositoryImpl.java
new file mode 100644
index 0000000000..f782d69e1e
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/dao/BookRepositoryImpl.java
@@ -0,0 +1,46 @@
+package org.baeldung.persistence.criteria.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.baeldung.persistence.criteria.model.Book;
+import org.baeldung.persistence.criteria.repository.BookRepositoryCustom;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class BookRepositoryImpl implements BookRepositoryCustom {
+
+ private EntityManager em;
+
+ public BookRepositoryImpl(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public List findBooksByAuthorNameAndTitle(String authorName, String title) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+ CriteriaQuery cq = cb.createQuery(Book.class);
+
+ Root book = cq.from(Book.class);
+ List predicates = new ArrayList<>();
+
+ if (authorName != null) {
+ predicates.add(cb.equal(book.get("author"), authorName));
+ }
+ if (title != null) {
+ predicates.add(cb.like(book.get("title"), "%" + title + "%"));
+ }
+ cq.where(predicates.toArray(new Predicate[0]));
+
+ TypedQuery query = em.createQuery(cq);
+ return query.getResultList();
+ }
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/model/Book.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/model/Book.java
new file mode 100644
index 0000000000..beb6c0190c
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/model/Book.java
@@ -0,0 +1,36 @@
+package org.baeldung.persistence.criteria.model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Book {
+
+ @Id
+ private Long id;
+
+ private String title;
+
+ private String author;
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepository.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepository.java
new file mode 100644
index 0000000000..af30ae461e
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepository.java
@@ -0,0 +1,9 @@
+package org.baeldung.persistence.criteria.repository;
+
+import org.baeldung.persistence.criteria.model.Book;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface BookRepository extends JpaRepository, BookRepositoryCustom, JpaSpecificationExecutor {
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepositoryCustom.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepositoryCustom.java
new file mode 100644
index 0000000000..35330cfa3c
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookRepositoryCustom.java
@@ -0,0 +1,11 @@
+package org.baeldung.persistence.criteria.repository;
+
+import java.util.List;
+
+import org.baeldung.persistence.criteria.model.Book;
+
+public interface BookRepositoryCustom {
+
+ List findBooksByAuthorNameAndTitle(String authorName, String title);
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookService.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookService.java
new file mode 100644
index 0000000000..7b1aff857e
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookService.java
@@ -0,0 +1,25 @@
+package org.baeldung.persistence.criteria.repository;
+
+import static org.baeldung.persistence.criteria.repository.BookSpecifications.hasAuthor;
+import static org.baeldung.persistence.criteria.repository.BookSpecifications.titleContains;
+import static org.springframework.data.jpa.domain.Specifications.where;
+
+import java.util.List;
+
+import org.baeldung.persistence.criteria.model.Book;
+import org.springframework.stereotype.Service;
+
+@Service
+public class BookService {
+
+ private BookRepository bookRepository;
+
+ public BookService(BookRepository bookRepository) {
+ this.bookRepository = bookRepository;
+ }
+
+ public List query(String author, String title) {
+ return bookRepository.findAll(where(hasAuthor(author)).and(titleContains(title)));
+ }
+
+}
diff --git a/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookSpecifications.java b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookSpecifications.java
new file mode 100644
index 0000000000..392b750977
--- /dev/null
+++ b/persistence-modules/spring-jpa/src/main/java/org/baeldung/persistence/criteria/repository/BookSpecifications.java
@@ -0,0 +1,16 @@
+package org.baeldung.persistence.criteria.repository;
+
+import org.baeldung.persistence.criteria.model.Book;
+import org.springframework.data.jpa.domain.Specification;
+
+public class BookSpecifications {
+
+ public static Specification hasAuthor(String author) {
+ return (book, cq, cb) -> cb.equal(book.get("author"), author);
+ }
+
+ public static Specification titleContains(String title) {
+ return (book, cq, cb) -> cb.like(book.get("title"), "%" + title + "%");
+ }
+
+}
diff --git a/spring-core/src/main/java/com/baeldung/event/listener/ContextEventListener.java b/spring-core/src/main/java/com/baeldung/event/listener/ContextEventListener.java
new file mode 100644
index 0000000000..a2603bb95c
--- /dev/null
+++ b/spring-core/src/main/java/com/baeldung/event/listener/ContextEventListener.java
@@ -0,0 +1,24 @@
+package com.baeldung.event.listener;
+
+import org.springframework.context.event.ContextStartedEvent;
+import org.springframework.context.event.ContextStoppedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ContextEventListener {
+
+ @Order(2)
+ @EventListener
+ public void handleContextRefreshEvent(ContextStartedEvent ctxStartEvt) {
+ System.out.println("Context Start Event received.");
+ }
+
+ @Order(1)
+ @EventListener(classes = { ContextStartedEvent.class, ContextStoppedEvent.class })
+ public void handleMultipleEvents() {
+ System.out.println("Multi-event listener invoked");
+ }
+
+}
diff --git a/spring-core/src/main/java/com/baeldung/event/listener/EventConfig.java b/spring-core/src/main/java/com/baeldung/event/listener/EventConfig.java
new file mode 100644
index 0000000000..f2a3af7640
--- /dev/null
+++ b/spring-core/src/main/java/com/baeldung/event/listener/EventConfig.java
@@ -0,0 +1,10 @@
+package com.baeldung.event.listener;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan(basePackages = "com.baeldung.event.listener")
+public class EventConfig {
+
+}
diff --git a/spring-core/src/main/java/com/baeldung/event/listener/SpringRunner.java b/spring-core/src/main/java/com/baeldung/event/listener/SpringRunner.java
new file mode 100644
index 0000000000..bbe4693900
--- /dev/null
+++ b/spring-core/src/main/java/com/baeldung/event/listener/SpringRunner.java
@@ -0,0 +1,12 @@
+package com.baeldung.event.listener;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+public class SpringRunner {
+
+ public static void main(String[] args) {
+ ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(EventConfig.class);
+ ctx.start();
+ }
+}
diff --git a/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/controller/CourseRegistrationController.java b/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/controller/CourseRegistrationController.java
new file mode 100644
index 0000000000..2d2b5906e1
--- /dev/null
+++ b/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/controller/CourseRegistrationController.java
@@ -0,0 +1,30 @@
+package com.baeldung.thymeleaf.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.baeldung.thymeleaf.model.Course;
+
+/**
+ * Handles requests for the student model.
+ *
+ */
+@Controller
+public class CourseRegistrationController {
+
+ @RequestMapping(value = "/registerCourse", method = RequestMethod.POST)
+ public String register(@ModelAttribute Course course, Model model) {
+ model.addAttribute("successMessage", "You have successfully registered for course: " + course.getName() + ".");
+ return "courseRegistration.html";
+ }
+
+ @RequestMapping(value = "/registerCourse", method = RequestMethod.GET)
+ public String register(Model model) {
+ model.addAttribute("course", new Course());
+ return "courseRegistration.html";
+ }
+
+}
diff --git a/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/model/Course.java b/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/model/Course.java
new file mode 100644
index 0000000000..df2e9cd097
--- /dev/null
+++ b/spring-thymeleaf/src/main/java/com/baeldung/thymeleaf/model/Course.java
@@ -0,0 +1,52 @@
+package com.baeldung.thymeleaf.model;
+
+import java.util.Date;
+
+public class Course {
+
+ private String name;
+ private String description;
+ private Date startDate;
+ private Date endDate;
+ private String teacher;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(Date startDate) {
+ this.startDate = startDate;
+ }
+
+ public Date getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(Date endDate) {
+ this.endDate = endDate;
+ }
+
+ public String getTeacher() {
+ return teacher;
+ }
+
+ public void setTeacher(String teacher) {
+ this.teacher = teacher;
+ }
+}
diff --git a/spring-thymeleaf/src/main/webapp/WEB-INF/views/courseRegistration.html b/spring-thymeleaf/src/main/webapp/WEB-INF/views/courseRegistration.html
new file mode 100644
index 0000000000..cfce92d055
--- /dev/null
+++ b/spring-thymeleaf/src/main/webapp/WEB-INF/views/courseRegistration.html
@@ -0,0 +1,42 @@
+
+
+
+Register for course
+
+
+
+
+ Register Here
+
+
+
diff --git a/spring-thymeleaf/src/main/webapp/WEB-INF/views/home.html b/spring-thymeleaf/src/main/webapp/WEB-INF/views/home.html
index b458f7270c..25ff6a19bb 100644
--- a/spring-thymeleaf/src/main/webapp/WEB-INF/views/home.html
+++ b/spring-thymeleaf/src/main/webapp/WEB-INF/views/home.html
@@ -24,6 +24,9 @@
|
+
+ |
+