I am creating a Spring Boot application using the H2 database. I am constantly getting the following error:
Table "THINGS_TO_DO" not found; SQL statement:
insert into things_to_do (id, name, verified) values (1, 'TestUser1', 1) [42102-197]
And, I feel this is logical since I don't know where to pass this table name in the application. Also, what should the table name be - is there some specific name that the table must have?
My ThingsToDo.java is like below:
package me.hiboy.springboot.microservice.example.todo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="things_to_do")
public class ThingsToDo {
#Id
private Long id;
#Column(name="name")
private String name;
#Column(name="verified")
private int verificationStatus;
private String task;
public ThingsToDo() {
}
public ThingsToDo(Long id, String name, int verificationStatus, String task) {
super();
this.id=id;
this.name=name;
this.verificationStatus=verificationStatus;
this.task=task;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public int getVerificationStatus() {
return verificationStatus;
}
public String getTask() {
return task;
}
}
The controller ThingsToDoController.java is as follows:
package me.hiboy.springboot.microservice.example.todo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ThingsToDoController {
#Autowired
ThingsToDoRepository repository;
#GetMapping("/")
public String index() {
return "Hello from the ToDo Controller\n";
}
#GetMapping("/todo/{name}")
public ThingsToDo getThingsToDo(#PathVariable String name) {
ThingsToDo thingToDo=repository.findByName(name);
return thingToDo;
}
}
Repository ThingsToDoRepository is:
package me.hiboy.springboot.microservice.example.todo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ThingsToDoRepository extends JpaRepository<ThingsToDo, Long> {
ThingsToDo findByName(String name);
}
Application.properties is:
spring.application.name=todo-service
server.port=8080
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:mydb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.platform=h2
spring.datasource.initialize=true
data.sql is:
insert into things_to_do (id, name, verified) values (1, 'TestUser1', 1);
insert into things_to_do (id, name, verified) values (2, 'TestUser2', 0);
I don't think pom.xml is required - in case it is, kindly lemme know and I will post that as well. Thanks.
Edit:
The one with the main() method is here:
package me.hiboy.springboot.microservice.example.todo.springbootmicroservicetodoservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootMicroserviceTodoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMicroserviceTodoServiceApplication.class, args);
}
}
Edit: All the answers given so far do not help at all.
Follow the package name as
If your main class is in the com.example package
then all packages in your application will be following this package
as for new entity, the package will be
com.example.entity
You need to use proper package names as I can see you Application class and other classes do not follow the package naming convention.
Plus If I just want to insert simple test data I often implement a ApplicationRunner. Implementations of this interface are run at application startup and can use e.g. a autowired repository to insert some test data.
Your implementation would look like this:
#Component
public class DataLoader implements ApplicationRunner {
#Autowired
ThingsToDoRepository repository;
#Autowired
public DataLoader(ThingsToDoRepository repository) {
this.repository = repository;
}
public void run(ApplicationArguments args) {
repository.save(new ThingsToDo(1, 'TestUser1', 1));
repository.save(new ThingsToDo(2, 'TestUser2', 0));
}
}
You are getting this error because there is no table with such name.
You could try to add spring.jpa.hibernate.ddl-auto=create-drop to your .properties file. Then each time you run your app it should generate that table using your entity.
Or you need to create a table with name things_to_do manually and then when you run your app it should work. For this, you need to add
/src/main/resources/schema.sql
create table things_to_do
(
id integer not null,
/*all the rest columns */
);
Another thing is that for Spring to find your components (like repository or service etc.) it scans packages. And auto-configs scan the package where your main class is located and all the nested packages.
So if you don't want to set manually where your classes are located, you need to follow this structure!
Example:
my.main.package // here is your main class
my.main.package.entities // here are your entities
my.main.package.repositories // your repos
my.main.package.services // services
This is just an example, it does not mean you should provide the same names, just follow the convention. Hope it is clear what I mean by package structure :)
Usually, you would refer to your table by its entity name, not the actual table name. In this case it would be ThingsToDo.
But in this particular case, you're overriding this by giving your entity another name:
#Entity
#Table(name="things_to_do")
public class ThingsToDo {
That's why you should work with "things_to_do" in your query, or remove the name statement.
Related
I am completely new to spring boot and I am now trying to insert some data to my database from spring boot. What is the correct way to do this?
file structure
NewUser.java
package com.example.demo.pojo;
public class NewUser {
private String CompanyName;
public String getCompanyName() {
return CompanyName;
}
public void setCompanyName(String CompanyName) {
this.CompanyName = CompanyName;
}
}
RegistrationController.java
package com.example.demo.controller;
import com.example.demo.result.Result;
import com.example.demo.pojo.NewUser;
import org.springframework.beans.factory.annotation.Autowired;
import com.example.demo.service.RegistrationService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#RestController
public class RegistrationController {
#CrossOrigin
#PostMapping(value = "api/registration")
#ResponseBody
public Result registration(#RequestBody NewUser user) {
System.out.println(user.toString());
return new Result(200);
}
}
Above is how I get data from frontend and below is what I tried to insert data. How should I call the service to insert data?
AccApplMapper.java
package com.example.demo.mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
#Mapper
public interface AccApplMapper {
#Insert("INSERT INTO ACCT_APPL(ENG_COMP_NAME) VALUES(#{CompanyName}")
public int addAcctAppl(#Param("CompanyName") String CompanyName);
}
RegistrationService.java
package com.example.demo.service;
import com.example.demo.mapper.AccApplMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class RegistrationService {
private AccApplMapper accApplMapper;
public int addAcctAppl(String CompanyName) {
return accApplMapper.addAcctAppl(CompanyName);
}
}
Based on question above, you can modify your registration method in RegistrationController something like below, along with using Autowired annotation in controller :
public class RegistrationController {
..
#Autowired
RegistrationService registrationService;
...
public Result registration(#RequestBody NewUser user) {
System.out.println(user.toString());
if(user!=null && user.getCompanyName()!=null) {
int insert = registrationService.addAcctAppl(user.getCompanyName());
return insert>0 ? new Result(200) : new Result(500);
}
else {
return new Result(400);
}
}
here based on input data, calling service method & returning appropriate httpStatus code as argument to Result.
Hello friend I suggest you use Spring Data JPA dependency, it makes alot easier to perform any database operation.
Spring Data JPA provides repository support for the Java Persistence
API (JPA). It eases development of applications that need to access
JPA data sources.
Here are some good reference links
Spring Data JPA - Reference Documentation
Introduction to Spring Data JPA
I'm having a database with existing tables. One of these is called AKTIVE_AUFTRAEGE with the id "AKTIVE_AUFTRAGE_ID" and the second field "ROBOTER_AUFTRAG_ID". I want to read the data using Spring data. I followed some tutorials an my code looks like this:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:sqlserver://****;databaseName=****;schema=dbo
spring.datasource.username=****
spring.datasource.password=****
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=true
spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
and
package hello;
import javax.persistence.*;
#Entity
#Table(name = "AKTIVE_AUFTRAEGE")
public class AktiveAuftraege {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="AKTIVE_AUFTRAEGE_ID")
private Integer auftragID;
private Integer ROBOTER_AUFTRAG_ID;
... getter and setter
}
and
package hello;
import org.springframework.data.repository.CrudRepository;
public interface AuftraegeRepsository extends CrudRepository<AktiveAuftraege, Integer> {
AktiveAuftraege findByauftragID(Integer aktive_auftraege_id);
}
and
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
#Controller
public class Test {
#Autowired
private AuftraegeRepsository auftraegeRepsository;
public void testAll(){
if (auftraegeRepsository != null) {
Iterable<AktiveAuftraege> results = auftraegeRepsository.findAll();
for (AktiveAuftraege e : results) {
System.out.println(e);
}
} else {
System.out.println("ISNULL!");
}
}
}
and the main
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
Test t = new Test();
t.testAll();
}
}
I'm very new to Spring data and have a couple of questions:
How can I check if the application has a connection to the database?
Why is the autowired repository always null?
I assume the problem mentioned problem is caused in the application class with the new operator. How can I avoid this? (Or is this fine?)
Do I need to implement all variables of a table in a class or can I just implement the ones I want to get back and igore the other columns?
How can I check if the application has a connection to the database?
If the application starts up correctly, it means you have a connection to the database
Why is the autowired repository always null?
Because you instantiate the Test class yourself, and Spring is thus out of the equation, and thus doesn't inject anything in the object you created behind its back, since it doesn't even know about it.
How can I avoid this?
By getting it from the ApplicationContext returned by SpringApplication.run(), or by creating a bean of type CommandLineRunner and injecting the Test inside it. Check the Spring Boot documentation.
Do I need to implement all variables of a table in a class or can I just implement the ones I want to get back and igore the other columns?
If you just want to read from that table, having only a subset of the columns is fine. But if you want to insert data in it, the other columns will be ignored, and will thus always have their default value (or null if there is no default value).
I am using Micronaut as framework for developing an AWS Java Lambda.
Micronaut supports #Value for reading, well, "values".
#io.micronaut.context.annotation.Factory
public class SomeFactory {
public SomeFactory(
#io.micronaut.context.annotation.Value("${NameOfValue}")
final String value) {
...
}
...
}
When testing, I want to set "NameOfValue" to a specific value, how can I do that?
#io.micronaut.test.annotation.MicronautTest
class SomeLambdaIT {
#org.junit.jupiter.api.Test
void aTest() {
// When this test runs, "NameOfValue" shall be set to a specific value
}
}
When testing, I want to set "NameOfValue" to a specific value, how can
I do that?
You have a number of options.
One option is to define src/test/resources/application-test.yml and define the config setting there. That file will only be loaded in the test environment and any settings defined in that file will supersede values defined in src/main/resources/application.yml.
Another option that might make sense if you only want the special setting in play for this particular test is you can do something like this...
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
#MicronautTest(propertySources = "classpath:some-special-test-props.properties")
public class SomeTest {
#Test
void someTest() {
// ...
}
}
Then define src/test/resources/some-special-test-props.properties and assign the value there.
Yet another option is to mark your test with #Property:
import io.micronaut.context.annotation.Property;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
#MicronautTest
#Property(name="some.config.value", value = "My Test Value")
public class SomeTest {
#Test
void someTest() {
// ...
}
}
I hope that helps.
EDIT
A comment below includes "I did give it a try, but the #Property solution nor the some-special-test-props.properties works in my case.". I have created a sample app demonstrating each of these techniques. See the project at https://github.com/jeffbrown/markusschultevalue.
https://github.com/jeffbrown/markusschultevalue/blob/8131e96492356180e2c7fade09603bd41f8c8829/src/main/java/markusschultevalue/SomeWidget.java
package markusschultevalue;
public class SomeWidget {
private final String name;
public SomeWidget(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
https://github.com/jeffbrown/markusschultevalue/blob/master/src/main/java/markusschultevalue/SomeFactory.java
package markusschultevalue;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Value;
#Factory
public class SomeFactory {
private final String name;
// there are better ways to do this but
// this is consistent with the code in the
// question being asked...
public SomeFactory(#Value("${some.config.value}") String name) {
this.name = name;
}
#Bean
public SomeWidget createWidget() {
return new SomeWidget(name);
}
}
https://github.com/jeffbrown/markusschultevalue/blob/8131e96492356180e2c7fade09603bd41f8c8829/src/test/java/markusschultevalue/PropertyAnnotationTest.java
package markusschultevalue;
import io.micronaut.context.annotation.Property;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.junit.jupiter.api.Assertions.assertEquals;
#MicronautTest
#Property(name="some.config.value", value="Some Widget Name")
public class PropertyAnnotationTest {
#Inject
SomeWidget someWidget;
#Test
void testWidget() {
assertEquals("Some Widget Name", someWidget.getName());
}
}
https://github.com/jeffbrown/markusschultevalue/blob/8131e96492356180e2c7fade09603bd41f8c8829/src/test/java/markusschultevalue/ConfigFileTest.java
package markusschultevalue;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.junit.jupiter.api.Assertions.assertEquals;
// This will load the config value
// from src/test/resources/some-widget-test-config.yml
#MicronautTest(propertySources = "classpath:some-widget-test-config.yml")
public class ConfigFileTest {
#Inject
SomeWidget someWidget;
#Test
void testWidget() {
assertEquals("Some Other Widget Name", someWidget.getName());
}
}
https://github.com/jeffbrown/markusschultevalue/blob/8131e96492356180e2c7fade09603bd41f8c8829/src/test/resources/some-widget-test-config.yml
some:
config:
value: Some Other Widget Name
Note that in your example you are referencing a config variable with "${NameOfValue}". If that is actually the name of your config variable, note that in code you need to reference that in valid kebab-case which would be "${name-of-value}".
I hope that helps.
I have an abstract class (database mapping) implementing an interface where default implementations are injected at runtime (this is part of another library and cannot be changed).
I want to override one of the default implementation via a proxy (as that seems like the way to override this).
public abstract class Table1 implements Storable<Table1>
{
#Sequence("ID_SEQUENCE")
#Alias("ID")
public abstract String getID();
public abstract void setID(String ID);
#Alias("NAME")
public abstract String getAvailabilityZone();
public abstract void setAvailabilityZone(String value);
}
public interface Storable<S extends Storable<S>> {
//a bunch of method definition.
boolean tryLoad() throws Exception;
}
Let's say I want to override tryLoad() method to do my own things instead of what the generated code provides. Given the nature of the library, it is not something I can achieve by simple #Override.
The simple way this is currently used is as following:
public void method() {
Table1 t = Repository.storageFor(Table1.class).prepare();
t.setName( "temp" );
if (!t.tryLoad())
t.tryInsert();
}
I want to proxy tryLoad() without making changes in all the methods across the whole codebase - that would be to get proxied instance instead of actual one and perform the operation on that.
Is there any recommended way to achieve this?
Thanks!
I woke up last night and felt bored, so despite your lack of feedback I created a little Carbonado showcase project and shared it on GitHub. I made three commits:
Initial commit with Maven project already prepared for AspectJ and a JUnit test for me to find out how Carbonado actually works, because I had never used it before.
Add failing unit test for behaviour of tryLoad() expected to be provided by aspect.
Add aspect to make unit test pass. Aspect hooks into tryLoad() and auto-creates non-existent record. I do not know if I guessed right what you actually wanted to achieve, but if it was a different thing, just change the aspect implementation.
Sample code
Carbonado storable:
package de.scrum_master.app;
import com.amazon.carbonado.Nullable;
import com.amazon.carbonado.PrimaryKey;
import com.amazon.carbonado.Storable;
#PrimaryKey("ID")
public interface StoredMessage extends Storable<StoredMessage> {
long getID();
void setID(long id);
#Nullable String getMessage();
void setMessage(String message);
}
Aspect:
package de.scrum_master.aspect;
import com.amazon.carbonado.Storable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class CarbonadoAspect {
#Around("call(boolean tryLoad()) && target(storable)")
public boolean tryInsertIfNotFound(ProceedingJoinPoint thisJoinPoint, Storable storable) throws Throwable {
System.out.println(thisJoinPoint);
if ((boolean) thisJoinPoint.proceed())
return true;
System.out.println("Not found: " + storable + " -> inserting");
return storable.tryInsert();
}
}
JUnit test:
package de.scrum_master.app;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;
import com.amazon.carbonado.Repository;
import com.amazon.carbonado.RepositoryException;
import com.amazon.carbonado.Storage;
import com.amazon.carbonado.SupportException;
import com.amazon.carbonado.repo.map.MapRepositoryBuilder;
import de.scrum_master.app.StoredMessage;
public class CarbonadoTest {
private Repository repo;
private Storage<StoredMessage> storage;
StoredMessage message;
#Before
public void setUp() throws Exception {
repo = MapRepositoryBuilder.newRepository();
storage = repo.storageFor(StoredMessage.class);
message = storage.prepare();
}
#After
public void tearDown() throws Exception {
repo.close();
repo = null;
storage = null;
message = null;
}
// (...)
#Test
public void aspectCreatesNonExistentRecord() throws SupportException, RepositoryException {
message.setID(1);
// Without the aspect this would be false
assertTrue(message.tryLoad());
assertEquals(message.getID(), 1);
assertEquals(message.getMessage(), null);
}
}
Enjoy!
I can connect my neo4j server using the neo4jClient and every thing works fine.
For unit testing scenarios I want to use a different local server to perform unit testing to my neo4j DAL layer.
so I tried the neo4j embedded version. I can create nodes and query them using the deprecated
GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH)
ExecutionEngine engine = new ExecutionEngine(graphDb);
1)What is the new way to create embedded neo4j instance?
2)How can I query the embedded using the neo4jClient? tried connecting with local host but with no success(is the embedded version has web host?)
What is the new way to create embedded neo4j instance?
You actually already did it with the code in your question!
The documentation on the hello world app for embedded neo4j shows this code:
graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( DB_PATH );
So you're already there.
How can I query the embedded using the neo4jClient? tried connecting with local host but with no success(is the embedded version has web host?)
If by the "neo4jclient" you mean the tool that people use in their browsers to visualize graphs, here's how to do that.
When you create an embedded neo4j database, the DB_PATH is important. Basically you just end up creating a directory by that name locally.
The neo4j browser application can be pointed at any graph path. It doesn't run embedded, it runs along with the server, so practically speaking what you'll do is configure the server to point to that directory you created for the embedded DB, and then it'll work.
See this documentation, you need to set:
org.neo4j.server.database.location=data/graph.db
Where data/graph.db is the same as DB_PATH in your embedded example.
Check with this example, it will help you.
Application.java
package hello;
import java.io.File;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.core.GraphDatabase;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Configuration
#EnableNeo4jRepositories(basePackages = "hello")
static class ApplicationConfig extends Neo4jConfiguration {
public ApplicationConfig() {
setBasePackage("hello");
}
#Bean
GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("C:/neo4j-community-2.1.7/data/graph.db");
}
}
#Autowired PersonRepository personRepository;
#Autowired GraphDatabase graphDatabase;
public void run(String... args) throws Exception {
Person greg = new Person("Greg");
Person roy = new Person("Roy");
Person craig = new Person("Craig");
Person abc=new Person("ABC");
Person def=new Person("DEF");
Person ghi=new Person("GHI");
/*System.out.println("Before linking up with Neo4j...");*/
for (Person person : new Person[] { greg, roy, craig,abc,def,ghi }) {
/* System.out.println(person);*/
}
Transaction tx = graphDatabase.beginTx();
try {
personRepository.save(greg);
personRepository.save(roy);
personRepository.save(craig);
personRepository.save(abc);
personRepository.save(def);
personRepository.save(ghi);
greg = personRepository.findByName(greg.name);
greg.worksWith(roy);
greg.worksWith(craig);
personRepository.save(greg);
roy = personRepository.findByName(roy.name);
roy.worksWith(craig);
// We already know that roy works with greg
personRepository.save(roy);
// We already know craig works with roy and greg
// System.out.println("Lookup each person by name...");
for (String name : new String[] { greg.name, roy.name, craig.name }) {
System.out.println("--->"+personRepository.findByName(name));
}
// System.out.println("Looking up who works with Greg...");
for (Person person : personRepository.findByTeammatesName("Greg")) {
System.out.println("==>>"+person.name + " works with Greg.");
}
tx.success();
} finally {
tx.close();
}
}
public static void main(String[] args) throws Exception {
FileUtils.deleteRecursively(new File("C:/neo4j-community-2.1.7/data/graph.db"));
SpringApplication.run(Application.class, args);
}
}
create a pojo file, Person.java
package hello;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;
#NodeEntity
public class Person {
#GraphId Long id;
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
#RelatedTo(type="TEAMMATE", direction=Direction.BOTH)
public #Fetch Set<Person> teammates;
public void worksWith(Person person) {
if (teammates == null) {
teammates = new HashSet<Person>();
}
teammates.add(person);
}
public String toString() {
String results = name + "'s teammates include\n";
if (teammates != null) {
for (Person person : teammates) {
results += "\t- " + person.name + "\n";
}
}
return results;
}
}
and create PersonRepository.java
package hello;
import org.springframework.data.repository.CrudRepository;
public interface PersonRepository extends CrudRepository<Person, String> {
Person findByName(String name);
Iterable<Person> findByTeammatesName(String name);
}