Merge branch 'master' into Mercator

This commit is contained in:
Grigorios Dimopoulos 2018-10-31 12:55:15 +02:00
commit 2e4c950e4a
28 changed files with 1101 additions and 26 deletions

View File

@ -0,0 +1,84 @@
package com.baeldung.properties;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.Test;
public class MergePropertiesUnitTest {
@Test
public void givenTwoProperties_whenMergedUsingIteration_thenAllPropertiesInResult() {
Properties globalProperties = mergePropertiesByIteratingKeySet(propertiesA(), propertiesB());
testMergedProperties(globalProperties);
}
@Test
public void givenTwoProperties_whenMergedUsingPutAll_thenAllPropertiesInResult() {
Properties globalProperties = mergePropertiesByUsingPutAll(propertiesA(), propertiesB());
testMergedProperties(globalProperties);
}
@Test
public void givenTwoProperties_whenMergedUsingStreamAPI_thenAllPropertiesInResult() {
Properties globalProperties = mergePropertiesByUsingStreamApi(propertiesB(), propertiesA());
testMergedProperties(globalProperties);
}
private Properties mergePropertiesByIteratingKeySet(Properties... properties) {
Properties mergedProperties = new Properties();
for (Properties property : properties) {
Set<String> propertyNames = property.stringPropertyNames();
for (String name : propertyNames) {
String propertyValue = property.getProperty(name);
mergedProperties.setProperty(name, propertyValue);
}
}
return mergedProperties;
}
private Properties mergePropertiesByUsingPutAll(Properties... properties) {
Properties mergedProperties = new Properties();
for (Properties property : properties) {
mergedProperties.putAll(property);
}
return mergedProperties;
}
private Properties mergePropertiesByUsingStreamApi(Properties... properties) {
return Stream.of(properties)
.collect(Properties::new, Map::putAll, Map::putAll);
}
private Properties propertiesA() {
Properties properties = new Properties();
properties.setProperty("application.name", "my-app");
properties.setProperty("application.version", "1.0");
return properties;
}
private Properties propertiesB() {
Properties properties = new Properties();
properties.setProperty("property-1", "sample property");
properties.setProperty("property-2", "another sample property");
return properties;
}
private void testMergedProperties(Properties globalProperties) {
assertThat("There should be 4 properties", globalProperties.size(), equalTo(4));
assertEquals("Property should be", globalProperties.getProperty("application.name"), "my-app");
assertEquals("Property should be", globalProperties.getProperty("application.version"), "1.0");
assertEquals("Property should be", globalProperties.getProperty("property-1"), "sample property");
assertEquals("Property should be", globalProperties.getProperty("property-2"), "another sample property");
}
}

View File

@ -61,6 +61,11 @@
<version>3.3.0</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>uy.kohesive.injekt</groupId>
<artifactId>injekt-core</artifactId>
<version>1.16.1</version>
</dependency>
</dependencies>
<properties>

View File

@ -1,10 +1,10 @@
package com.baeldung.datamapping
data class User(
val firstName: String,
val lastName: String,
val street: String,
val houseNumber: String,
val phone: String,
val age: Int,
val password: String)
val firstName: String,
val lastName: String,
val street: String,
val houseNumber: String,
val phone: String,
val age: Int,
val password: String)

View File

@ -3,10 +3,10 @@ package com.baeldung.datamapping
import kotlin.reflect.full.memberProperties
fun User.toUserView() = UserView(
name = "$firstName $lastName",
address = "$street $houseNumber",
telephone = phone,
age = age
name = "$firstName $lastName",
address = "$street $houseNumber",
telephone = phone,
age = age
)
fun User.toUserViewReflection() = with(::UserView) {

View File

@ -1,8 +1,8 @@
package com.baeldung.datamapping
data class UserView(
val name: String,
val address: String,
val telephone: String,
val age: Int
val name: String,
val address: String,
val telephone: String,
val age: Int
)

View File

@ -0,0 +1,60 @@
package com.baeldung.injekt
import org.slf4j.LoggerFactory
import uy.kohesive.injekt.*
import uy.kohesive.injekt.api.*
import java.util.*
class DelegateInjectionApplication {
companion object : InjektMain() {
private val LOG = LoggerFactory.getLogger(DelegateInjectionApplication::class.java)
@JvmStatic fun main(args: Array<String>) {
DelegateInjectionApplication().run()
}
override fun InjektRegistrar.registerInjectables() {
addFactory {
val value = FactoryInstance("Factory" + UUID.randomUUID().toString())
LOG.info("Constructing instance: {}", value)
value
}
addSingletonFactory {
val value = SingletonInstance("Singleton" + UUID.randomUUID().toString())
LOG.info("Constructing singleton instance: {}", value)
value
}
addSingletonFactory { App() }
}
}
data class FactoryInstance(val value: String)
data class SingletonInstance(val value: String)
class App {
private val instance: FactoryInstance by injectValue()
private val lazyInstance: FactoryInstance by injectLazy()
private val singleton: SingletonInstance by injectValue()
private val lazySingleton: SingletonInstance by injectLazy()
fun run() {
for (i in 1..5) {
LOG.info("Instance {}: {}", i, instance)
}
for (i in 1..5) {
LOG.info("Lazy Instance {}: {}", i, lazyInstance)
}
for (i in 1..5) {
LOG.info("Singleton {}: {}", i, singleton)
}
for (i in 1..5) {
LOG.info("Lazy Singleton {}: {}", i, lazySingleton)
}
}
}
fun run() {
Injekt.get<App>().run()
}
}

View File

@ -0,0 +1,37 @@
package com.baeldung.injekt
import org.slf4j.LoggerFactory
import uy.kohesive.injekt.*
import uy.kohesive.injekt.api.*
class KeyedApplication {
companion object : InjektMain() {
private val LOG = LoggerFactory.getLogger(KeyedApplication::class.java)
@JvmStatic fun main(args: Array<String>) {
KeyedApplication().run()
}
override fun InjektRegistrar.registerInjectables() {
val configs = mapOf(
"google" to Config("googleClientId", "googleClientSecret"),
"twitter" to Config("twitterClientId", "twitterClientSecret")
)
addPerKeyFactory<Config, String> {key -> configs[key]!! }
addSingletonFactory { App() }
}
}
data class Config(val clientId: String, val clientSecret: String)
class App {
fun run() {
LOG.info("Google config: {}", Injekt.get<Config>("google"))
LOG.info("Twitter config: {}", Injekt.get<Config>("twitter"))
}
}
fun run() {
Injekt.get<App>().run()
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.injekt
import org.slf4j.LoggerFactory
import uy.kohesive.injekt.*
import uy.kohesive.injekt.api.*
class ModularApplication {
class ConfigModule(private val port: Int) : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingleton(Config(port))
}
}
object ServerModule : InjektModule {
override fun InjektRegistrar.registerInjectables() {
addSingletonFactory { Server(Injekt.get()) }
}
}
companion object : InjektMain() {
private val LOG = LoggerFactory.getLogger(Server::class.java)
@JvmStatic fun main(args: Array<String>) {
ModularApplication().run()
}
override fun InjektRegistrar.registerInjectables() {
importModule(ConfigModule(12345))
importModule(ServerModule)
}
}
data class Config(
val port: Int
)
class Server(private val config: Config) {
fun start() {
LOG.info("Starting server on ${config.port}")
}
}
fun run() {
Injekt.get<Server>().start()
}
}

View File

@ -0,0 +1,48 @@
package com.baeldung.injekt
import org.slf4j.LoggerFactory
import uy.kohesive.injekt.*
import uy.kohesive.injekt.api.*
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
class PerThreadApplication {
companion object : InjektMain() {
private val LOG = LoggerFactory.getLogger(PerThreadApplication::class.java)
@JvmStatic fun main(args: Array<String>) {
PerThreadApplication().run()
}
override fun InjektRegistrar.registerInjectables() {
addPerThreadFactory {
val value = FactoryInstance(UUID.randomUUID().toString())
LOG.info("Constructing instance: {}", value)
value
}
addSingletonFactory { App() }
}
}
data class FactoryInstance(val value: String)
class App {
fun run() {
val threadPool = Executors.newFixedThreadPool(5)
for (i in 1..20) {
threadPool.submit {
val instance = Injekt.get<FactoryInstance>()
LOG.info("Value for thread {}: {}", Thread.currentThread().id, instance)
TimeUnit.MILLISECONDS.sleep(100)
}
}
threadPool.awaitTermination(10, TimeUnit.SECONDS)
}
}
fun run() {
Injekt.get<App>().run()
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.injekt
import org.slf4j.LoggerFactory
import uy.kohesive.injekt.*
import uy.kohesive.injekt.api.*
class SimpleApplication {
companion object : InjektMain() {
private val LOG = LoggerFactory.getLogger(Server::class.java)
@JvmStatic fun main(args: Array<String>) {
SimpleApplication().run()
}
override fun InjektRegistrar.registerInjectables() {
addSingleton(Config(12345))
addSingletonFactory { Server(Injekt.get()) }
}
}
data class Config(
val port: Int
)
class Server(private val config: Config) {
fun start() {
LOG.info("Starting server on ${config.port}")
}
}
fun run() {
Injekt.get<Server>().start()
}
}

View File

@ -22,22 +22,22 @@ class UserTest {
private fun buildUser(): User {
return User(
"Java",
"Duke",
"Javastreet",
"42",
"1234567",
30,
"s3cr37"
"Java",
"Duke",
"Javastreet",
"42",
"1234567",
30,
"s3cr37"
)
}
private fun assertUserView(pr: UserView) {
assertAll(
{ assertEquals("Java Duke", pr.name) },
{ assertEquals("Javastreet 42", pr.address) },
{ assertEquals("1234567", pr.telephone) },
{ assertEquals(30, pr.age) }
{ assertEquals("Java Duke", pr.name) },
{ assertEquals("Javastreet 42", pr.address) },
{ assertEquals("1234567", pr.telephone) },
{ assertEquals(30, pr.age) }
)
}
}

View File

@ -0,0 +1,18 @@
package org.baeldung.caching.eviction.controllers;
import org.baeldung.caching.eviction.service.CachingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CachingController {
@Autowired
CachingService cachingService;
@GetMapping("clearAllCaches")
public void clearAllCaches() {
cachingService.evictAllCaches();
}
}

View File

@ -0,0 +1,53 @@
package org.baeldung.caching.eviction.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class CachingService {
@Autowired
CacheManager cacheManager;
public void putToCache(String cacheName, String key, String value) {
cacheManager.getCache(cacheName).put(key, value);
}
public String getFromCache(String cacheName, String key) {
String value = null;
if (cacheManager.getCache(cacheName).get(key) != null) {
value = cacheManager.getCache(cacheName).get(key).get().toString();
}
return value;
}
@CacheEvict(value = "first", key = "#cacheKey")
public void evictSingleCacheValue(String cacheKey) {
}
@CacheEvict(value = "first", allEntries = true)
public void evictAllCacheValues() {
}
public void evictSingleCacheValue(String cacheName, String cacheKey) {
cacheManager.getCache(cacheName).evict(cacheKey);
}
public void evictAllCacheValues(String cacheName) {
cacheManager.getCache(cacheName).clear();
}
public void evictAllCaches() {
cacheManager.getCacheNames()
.parallelStream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
}
@Scheduled(fixedRate = 6000)
public void evictAllcachesAtIntervals() {
evictAllCaches();
}
}

View File

@ -0,0 +1,33 @@
package org.baeldung.nullibility;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
public class Person {
@NonNull
private String fullName;
@Nullable
private String nickName;
void setFullName(String fullName) {
if (fullName != null && fullName.isEmpty()) {
fullName = null;
}
this.fullName = fullName;
}
void setNickName(String nickName) {
if (nickName != null && nickName.isEmpty()) {
nickName = null;
}
this.nickName = nickName;
}
String getFullName() {
return fullName;
}
String getNickName() {
return nickName;
}
}

View File

@ -0,0 +1,6 @@
@NonNullApi
@NonNullFields
package org.baeldung.nullibility;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

View File

@ -0,0 +1,79 @@
package org.baeldung.caching.test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.baeldung.caching.eviction.service.CachingService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CacheEvictAnnotationIntegrationTest {
@Configuration
@EnableCaching
static class ContextConfiguration {
@Bean
public CachingService cachingService() {
return new CachingService();
}
@Bean
public CacheManager cacheManager(){
SimpleCacheManager cacheManager = new SimpleCacheManager();
List<Cache> caches = new ArrayList<>();
caches.add(cacheBean().getObject());
cacheManager.setCaches(caches );
return cacheManager;
}
@Bean
public ConcurrentMapCacheFactoryBean cacheBean(){
ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean();
cacheFactoryBean.setName("first");
return cacheFactoryBean;
}
}
@Autowired
CachingService cachingService;
@Before
public void before() {
cachingService.putToCache("first", "key1", "Baeldung");
cachingService.putToCache("first", "key2", "Article");
}
@Test
public void givenFirstCache_whenSingleCacheValueEvictRequested_thenEmptyCacheValue() {
cachingService.evictSingleCacheValue("key1");
String key1 = cachingService.getFromCache("first", "key1");
assertThat(key1, is(nullValue()));
}
@Test
public void givenFirstCache_whenAllCacheValueEvictRequested_thenEmptyCache() {
cachingService.evictAllCacheValues();
String key1 = cachingService.getFromCache("first", "key1");
String key2 = cachingService.getFromCache("first", "key2");
assertThat(key1, is(nullValue()));
assertThat(key2, is(nullValue()));
}
}

View File

@ -0,0 +1,96 @@
package org.baeldung.caching.test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.baeldung.caching.eviction.service.CachingService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CacheManagerEvictIntegrationTest {
@Configuration
static class ContextConfiguration {
@Bean
public CachingService cachingService() {
return new CachingService();
}
@Bean
public CacheManager cacheManager(){
SimpleCacheManager cacheManager = new SimpleCacheManager();
List<Cache> caches = new ArrayList<>();
caches.add(cacheBeanFirst().getObject());
caches.add(cacheBeanSecond().getObject());
cacheManager.setCaches(caches );
return cacheManager;
}
@Bean
public ConcurrentMapCacheFactoryBean cacheBeanFirst(){
ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean();
cacheFactoryBean.setName("first");
return cacheFactoryBean;
}
@Bean
public ConcurrentMapCacheFactoryBean cacheBeanSecond(){
ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean();
cacheFactoryBean.setName("second");
return cacheFactoryBean;
}
}
@Autowired
CachingService cachingService;
@Before
public void before() {
cachingService.putToCache("first", "key1", "Baeldung");
cachingService.putToCache("first", "key2", "Article");
cachingService.putToCache("second", "key", "Article");
}
@Test
public void givenFirstCache_whenSingleCacheValueEvictRequested_thenEmptyCacheValue() {
cachingService.evictSingleCacheValue("first", "key1");
String key1 = cachingService.getFromCache("first", "key1");
assertThat(key1, is(nullValue()));
}
@Test
public void givenFirstCache_whenAllCacheValueEvictRequested_thenEmptyCache() {
cachingService.evictAllCacheValues("first");
String key1 = cachingService.getFromCache("first", "key1");
String key2 = cachingService.getFromCache("first", "key2");
assertThat(key1, is(nullValue()));
assertThat(key2, is(nullValue()));
}
@Test
public void givenAllCaches_whenAllCacheEvictRequested_thenEmptyAllCaches() {
cachingService.evictAllCaches();
String key1 = cachingService.getFromCache("first", "key1");
assertThat(key1, is(nullValue()));
String key = cachingService.getFromCache("second", "key");
assertThat(key, is(nullValue()));
}
}

65
spring-boot-crud/pom.xml Normal file
View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung.spring-boot-crud</groupId>
<artifactId>spring-boot-crud</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<build>
<finalName>spring-boot-crud</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,24 @@
package com.baeldung.crud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages={"com.baeldung.crud"})
@EnableJpaRepositories(basePackages="com.baeldung.crud.repositories")
@EnableTransactionManagement
@EntityScan(basePackages="com.baeldung.crud.entities")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,66 @@
package com.baeldung.crud.controllers;
import com.baeldung.crud.UserRepository;
import com.baeldung.crud.entities.User;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class UserController {
private final UserRepository userRepository;
@Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping("/signup")
public String showSignUpForm(User user) {
return "add-user";
}
@PostMapping("/adduser")
public String addUser(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-user";
}
userRepository.save(user);
model.addAttribute("users", userRepository.findAll());
return "index";
}
@GetMapping("/edit/{id}")
public String showUpdateForm(@PathVariable("id") long id, Model model) {
User user = userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
model.addAttribute("user", user);
return "update-user";
}
@PostMapping("/update/{id}")
public String updateUser(@PathVariable("id") long id, @Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
user.setId(id);
return "update-user";
}
userRepository.save(user);
model.addAttribute("users", userRepository.findAll());
return "index";
}
@GetMapping("/delete/{id}")
public String deleteUser(@PathVariable("id") long id, Model model) {
User user = userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
userRepository.delete(user);
model.addAttribute("users", userRepository.findAll());
return "index";
}
}

View File

@ -0,0 +1,56 @@
package com.baeldung.crud.entities;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotBlank(message = "Name is mandatory")
private String name;
@NotBlank(message = "Email is mandatory")
private String email;
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
@Override
public String toString() {
return "User{" + "id=" + id + ", name=" + name + ", email=" + email + '}';
}
}

View File

@ -0,0 +1,13 @@
package com.baeldung.crud.repositories;
import com.baeldung.crud.entities.User;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findByName(String name);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add User</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css" integrity="sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz" crossorigin="anonymous">
<link rel="stylesheet" href="../css/shards.min.css">
</head>
<body>
<div class="container my-5">
<h2 class="mb-5">New User</h2>
<div class="row">
<div class="col-md-6">
<form action="#" th:action="@{/adduser}" th:object="${user}" method="post">
<div class="row">
<div class="form-group col-md-6">
<label for="name" class="col-form-label">Name</label>
<input type="text" th:field="*{name}" class="form-control" id="name" placeholder="Name">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger"></span>
</div>
<div class="form-group col-md-6">
<label for="email" class="col-form-label">Email</label>
<input type="text" th:field="*{email}" class="form-control" id="email" placeholder="Email">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-md-6 mt-5">
<input type="submit" class="btn btn-primary" value="Add User">
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Users</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css" integrity="sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz" crossorigin="anonymous">
<link rel="stylesheet" href="../css/shards.min.css">
</head>
<body>
<div th:switch="${users}" class="container my-5">
<div class="row">
<div class="col-md-6">
<h2 th:case="null">No users yet!</h2>
<div th:case="*">
<h2 class="my-5">Users</h2>
<table class="table table-striped table-responsive-md">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.name}"></td>
<td th:text="${user.email}"></td>
<td><a th:href="@{/edit/{id}(id=${user.id})}" class="btn btn-primary"><i class="fas fa-user-edit ml-2"></i></a></td>
<td><a th:href="@{/delete/{id}(id=${user.id})}" class="btn btn-primary"><i class="fas fa-user-times ml-2"></i></a></td>
</tr>
</tbody>
</table>
</div>
<p class="my-5"><a href="/signup" class="btn btn-primary"><i class="fas fa-user-plus ml-2"></i></a></p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Update User</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css" integrity="sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz" crossorigin="anonymous">
<link rel="stylesheet" href="../css/shards.min.css">
</head>
<body>
<div class="container my-5">
<h2 class="mb-5">Update User</h2>
<div class="row">
<div class="col-md-6">
<form action="#" th:action="@{/update/{id}(id=${user.id})}" th:object="${user}" method="post">
<div class="row">
<div class="form-group col-md-6">
<label for="name" class="col-form-label">Name</label>
<input type="text" th:field="*{name}" class="form-control" id="name" placeholder="Name">
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger"></span>
</div>
<div class="form-group col-md-6">
<label for="email" class="col-form-label">Email</label>
<input type="text" th:field="*{email}" class="form-control" id="email" placeholder="Email">
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="col-md-6 mt-5">
<input type="submit" class="btn btn-primary" value="Update User">
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,81 @@
package com.baeldung.crud;
import com.baeldung.crud.UserController;
import com.baeldung.crud.entities.User;
import com.baeldung.crud.UserRepository;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
public class UserControllerUnitTest {
private static UserController userController;
private static UserRepository mockedUserRepository;
private static BindingResult mockedBindingResult;
private static Model mockedModel;
@BeforeClass
public static void setUpUserControllerInstance() {
mockedUserRepository = mock(UserRepository.class);
mockedBindingResult = mock(BindingResult.class);
mockedModel = mock(Model.class);
userController = new UserController(mockedUserRepository);
}
@Test
public void whenCalledshowSignUpForm_thenCorrect() {
User user = new User("John", "john@domain.com");
assertThat(userController.showSignUpForm(user)).isEqualTo("add-user");
}
@Test
public void whenCalledaddUserAndValidUser_thenCorrect() {
User user = new User("John", "john@domain.com");
when(mockedBindingResult.hasErrors()).thenReturn(false);
assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("index");
}
@Test
public void whenCalledaddUserAndInValidUser_thenCorrect() {
User user = new User("John", "john@domain.com");
when(mockedBindingResult.hasErrors()).thenReturn(true);
assertThat(userController.addUser(user, mockedBindingResult, mockedModel)).isEqualTo("add-user");
}
@Test(expected = IllegalArgumentException.class)
public void whenCalledshowUpdateForm_thenIllegalArgumentException() {
assertThat(userController.showUpdateForm(0, mockedModel)).isEqualTo("update-user");
}
@Test
public void whenCalledupdateUserAndValidUser_thenCorrect() {
User user = new User("John", "john@domain.com");
when(mockedBindingResult.hasErrors()).thenReturn(false);
assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("index");
}
@Test
public void whenCalledupdateUserAndInValidUser_thenCorrect() {
User user = new User("John", "john@domain.com");
when(mockedBindingResult.hasErrors()).thenReturn(true);
assertThat(userController.updateUser(1l, user, mockedBindingResult, mockedModel)).isEqualTo("update-user");
}
@Test(expected = IllegalArgumentException.class)
public void whenCalleddeleteUser_thenIllegalArgumentException() {
assertThat(userController.deleteUser(1l, mockedModel)).isEqualTo("index");
}
}

View File

@ -0,0 +1,46 @@
package com.baeldung.crud;
import com.baeldung.crud.User;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class UserUnitTest {
@Test
public void whenCalledGetName_thenCorrect() {
User user = new User("Julie", "julie@domain.com");
assertThat(user.getName()).isEqualTo("Julie");
}
@Test
public void whenCalledGetEmail_thenCorrect() {
User user = new User("Julie", "julie@domain.com");
assertThat(user.getEmail()).isEqualTo("julie@domain.com");
}
@Test
public void whenCalledSetName_thenCorrect() {
User user = new User("Julie", "julie@domain.com");
user.setName("John");
assertThat(user.getName()).isEqualTo("John");
}
@Test
public void whenCalledSetEmail_thenCorrect() {
User user = new User("Julie", "julie@domain.com");
user.setEmail("john@domain.com");
assertThat(user.getEmail()).isEqualTo("john@domain.com");
}
@Test
public void whenCalledtoString_thenCorrect() {
User user = new User("Julie", "julie@domain.com");
assertThat(user.toString()).isEqualTo("User{id=0, name=Julie, email=julie@domain.com}");
}
}