Repository doesn't have a find-one-method declared - spring

I am trying to reproduce code sample from https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.basic (Basic web support)
I have some controller in Spring Web MVC application:
#Controller // This means that this class is a Controller
#RequestMapping(path = "/demo") // This means URL's start with /demo (after Application path)
#EnableSpringDataWebSupport
public class MainController {
#GetMapping(path = "/getResById/{id}")
#ResponseBody
public Tresource getResById(#PathVariable("id") Tresource tr, Model m) {
m.addAttribute(tr);
return tr;
}
}
The application main entry is the class with #SpringBootApplication annotation.
I also have repository class:
#Transactional(isolation = Isolation.READ_UNCOMMITTED)
public interface UserRepository extends Repository<Tresource, Long> {
// List<Tresource> findByBrief(String brief);
#Query("Select t.brief from Tresource t where t.resourceId=:resourceId")
String qqq(#Param("resourceId") Long resourceId);
Optional<Tresource> findDistinctByResourceIdOrBrief(Long resourceId, String brief);
#Query("Select i from Tresource t "
+ "inner join Tinstitution i on i.institutionId=t.instOwnerId "
+ "where t.resourceId=:resourceId")
Optional<Tinstitution> getResOwner(#Param("resourceId") Long resourceId);
}
This code gets exception
2018-03-21 14:33:15.977 WARN 11464 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to bind request element: org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.mycompany.eurofatcafns.db.Tresource'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#org.springframework.web.bind.annotation.PathVariable com.mycompany.eurofatcafns.db.Tresource] for value '2010015161038'; nested exception is java.lang.IllegalStateException: Repository doesn't have a find-one-method declared!
when I am accessing /demo/getResById/2010015161038 .
What am I doing wrong with this code? How to fix this code?
Thank you very much!

Solution found!
In my code sample, I used
public interface UserRepository extends Repository<Tresource, Long> {
but getOne(ID) and findOne(ID) are in the CrudRepository class.
So, I fixed my code to this code and everything works well now:
public interface UserRepository extends CrudRepository<Tresource, Long> {

Related

Spring Boot WebFlux Converter

I am trying to migrate my project from the Spring MVC to the Spring WebFlux.
The repository I am currently using is ReactiveCrudRepository.
In order to achieve the post-redirect-get pattern, which I have used within Spring MVC, I need to rewrite the current converter to work with ReactiveCrudRepository.
I was trying to do that with this aproach:
#Component
public class ObjByIdConverter implements Converter<String, Obj> {
#Autowired
private IObjRepository objRepository;
#Override
public Obj convert(String id) {
return objRepository.findById(id).block();
}
}
When I implement converter in this way, I am getting the following error:
block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-xxx.
When I was using CrudRepository instead of ReactiveCrudRepository everything was worked fine.
Is there a way to implement converter to work with ReactiveCrudRepository?
~~~ Edit 1 ~~~
The controller class:
#PostMapping
public Mono<String> processOrder(#ModelAttribute("newCar") Car car) {
webDataBinder.validate();
BindingResult bindingResult = webDataBinder.getBindingResult();
if (bindingResult.hasErrors()) {
return Mono.just("orderForm");
}
return this.carRepository.save(car).thenReturn("redirect:/");
}
The model class:
#Document(collection = "cars")
#ToString
#EqualsAndHashCode
public class Car {
#Id
private String id;
private List<Obj> objs = new ArrayList<>();
// constructor, getters, setters, ...
}
I am using the Thymeleaf view technology.
I have to provide the implementation for ObjByIdConverter because I am getting the following error message: [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'objs'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.app.model.Obj' for property 'objs[0]': no matching editors or conversion strategy found]
You should not use block in any case in reactive development. If you have ReactiveRepository and Spring Webflux, use them together with Mono/Flux from repository to controller to leverage the reactive way of doing.
But I think the main reason why you try to convert the result to a standard type is for the post-redirect-get pattern, could you detail this in the spring controller context ?

Im geting the error Parameter 0 of constructor required a single bean, but 2 were found but one of the beans "found" has been deleted by me

Im trying to set up a h2 database using jpa/jdbc, after creating an implemntation for a query interface using jpa as opposed to jdbc i am now getting the error:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Im unsure as to what has gone wrong and am not really sure where to go from here, i havent been able to find many people with a similar issue to me online.
My implementation using jdbc
#Repository
public class CharityRepoJDBC implements CharityRepository {
private JdbcTemplate jdbc;
private RowMapper<Charity> charityMapper;
#Autowired
public CharityRepoJDBC(JdbcTemplate aTemplate) {
jdbc = aTemplate;
charityMapper = (rs, i) -> new Charity(
rs.getLong("id"),
rs.getString("name"),
rs.getString("registration_id"),
rs.getString("acronym"),
rs.getString("purpose")
);
}
#Override
public List<Charity> findCharityBySearch(String searchTerm) {
String likeSearch = "%" + searchTerm + "%";
return jdbc.query(
"select id, acronym, name, purpose, logo_file_name, registration_id " +
"from charity " +
"where concat(name, acronym, purpose, registration_id) like ?",
new Object[]{likeSearch},
charityMapper);
}
#Override
public Optional<Charity> findById(Long id) {
return Optional.of(
jdbc.queryForObject(
"select id, acronym, name, purpose, logo_file_name, registration_id from charity where id=?",
new Object[]{id},
charityMapper)
);
}
}
Charity finder implementation:
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
public CharityQueries(CharityRepository repo) {
charityRepository = repo;
}
public Optional<Charity> findCharityByIndex(Integer index) {
return charityRepository.findById(index.longValue());
}
public List<Charity> findCharityBySearch(String searchTerm) {
return charityRepository.findCharityBySearch(searchTerm);
}
}
CharityFinder interface
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}
error log :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.nsa.charitystarter.service.CharityQueries required a single bean, but 2 were found:
- charityRepoJDBC: defined in file [C:\Users\V La Roche\Desktop\assessment-1-starter\out\production\classes\com\nsa\charitystarter\data\CharityRepoJDBC.class]
- charityRepoJPA: defined in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 0
You have following definition currently,
#Repository
public class CharityRepoJDBC implements CharityRepository {
And you are injecting CharityRepository in your service layer CharityQueries
#Service
public class CharityQueries implements CharityFinder {
private CharityRepository charityRepository;
Hence when you deploy your application the container is confused which bean you are trying to autowire into the service.
By default spring autowires by type and hence by that there are 2 beans which are qualified to be injected by spring container.
CharityRepository itself and other
CharityRepoJDBC
So you need to either explicitly tell container which bean you are trying to autowire in this case.
So you can try adding qualifiers as below to solve the issue.
#Service
public class CharityQueries implements CharityFinder {
#Qualifier("CharityRepoJDBC")
private CharityRepository charityRepository;
and at the same time modify your CharityRepoJDBC to be,
#Repository(value = "CharityRepoJDBC")
public class CharityRepoJDBC implements CharityRepository {
You seem to have the Spring Data JDBC starter on the classpath and the Spring Data JPA starter.
Spring Data JDBC has a bug which causes it to produce implementation for repository interfaces even if it shouldn't, thus you end up with one implementation from JPA and another one from JDBC.
If you really want to use Spring Data JDBC and Spring Data JPA you can limit the #EnableJdbcRepositories and #EnableJpaRepositories annotations using the include and exclude filters.
But from your code and the tags you used I suspect you might be not at all interested in Spring Data Jdbc but only in Spring Jdbc.
If this is the case look for a dependency spring-boot-starter-data-jdbc and change it to spring-boot-starter-jdbc.
In case all this Spring (Data) JDBC/JPA confuse you this question and its answers might help: Spring Data JDBC / Spring Data JPA vs Hibernate
I solved it putting #Primary annotation in the repository interface.
In your case it would be the following:
#Primary
public interface CharityFinder {
public Optional<Charity> findCharityByIndex(Integer index);
public List<Charity> findCharityBySearch(String searchTerm);
}

Missing URI template variable 'idFamille' for method parameter of type int

I have created an APi which retrieve data from a database. My API is globally functioning excepting one request, where I get the error of the title.
I don't understand because I have some other requests writed in the same way and only this one is not functionning
This is my REST services (VarianteRestServices)
#CrossOrigin("*")
#RestController
public class VarianteRestServices {
#Autowired
private VarianteRepository varianteRepository;
//This function is not working
#GetMapping(value="/listVariantesByFamille/{id}")
public List<Variante> listVariantesByFamilles(#PathVariable(name="idFamille") int idFamille){
return varianteRepository.findVarianteByFamille(idFamille);
}
And this is my Repository :
#CrossOrigin("*")
#RepositoryRestResource
public interface VarianteRepository extends JpaRepository<Variante, Integer>, JpaSpecificationExecutor<Variante> {
#Query(value = "SELECT v FROM Variante v WHERE v.famille.id = ?1")
#RestResource(path = "/byFamille")
public List<Variante> findVarianteByFamille(#Param("idF") Integer famille);
}
I call my API like this : http://localhost:8080/listVariantesByFamille/4
If anybody know why I get this error ?

Extend Spring Data Repository

I would like to introduce a <T> T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory) to all of my repositories.
So created a new Interface
#NoRepositoryBean
public interface ExtendedJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory);
}
.
public class ExtendedJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements ExtendedJpaRepository<T, ID> {
private final JpaEntityInformation entityInformation;
private final EntityManager entityManager;
public ExtendedJpaRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityInformation = entityInformation;
this.entityManager = entityManager;
}
#Override
public T findOrCreate(Supplier<Optional<T>> finder, Supplier<T> factory) {
throw new NotImplementedException("No implemented yet");
}
}
Then I use this interface in my concrete repositories, e.g. RecipeIngredientRepository:
public interface RecipeIngredientRepository extends ExtendedJpaRepository<RecipeIngredient, Long> {}
When I finally inject the repository to my service I get the following exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'recipeIngredientRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property find found for type RecipeIngredient! Did you mean 'id'?
It is searching for a find property in my entitiy RecipeIngredient. I did not want it to do this. I think this is related to JPA Query Methods. So I changed the name from findOrCreate to xxx to Bypass any query method detection - without success. It searches for a xxx property then.
What does make spring data look for this property?
I'm using org.springframework.boot:spring-boot-starter-data-jpa.
You need to specify your customized repository implementation via #EnableJpaRepositories(repositoryBaseClass = ExtendedJpaRepositoryImpl.class).
Take a look at the reference docs: Adding custom behavior to all repositories.
Adding to #md911de answer:
So what you can is to define a generic interface which has the base method that you want to have in all of your repositories:
#NoRepositoryBean
interface BaseGenericReactiveMongoRepository<T> :
ReactiveMongoRepository<T, String> {
fun patch(id: String, fields: Map<String, Any>): Mono<T>
}
Then you need to implement this and inform spring to use the implementation class for implementing the interface.
class SimpleBaseGenericReactiveMongoRepository<ENTITY>(
private val entityInformation: MappingMongoEntityInformation<ENTITY, String>,
private val template: ReactiveMongoTemplate
) : SimpleReactiveMongoRepository<ENTITY, String>(entityInformation, template),
BaseGenericReactiveMongoRepository<ENTITY> {
private val eventPublisher: ApplicationEventPublisher?
init {
val context = template.converter.mappingContext as MongoMappingContext
val indexCreator = MongoPersistentEntityIndexCreator(context) { collectionName ->
IndexOperationsAdapter.blocking(template.indexOps(collectionName))
}
eventPublisher = MongoMappingEventPublisher(indexCreator)
}
override fun patch(id: String, fields: Map<String, Any>): Mono<ENTITY> {
val collection = entityInformation.collectionName
val query = Query(Criteria.where("_id").`is`(id))
val document = Document()
return findById(id)
.flatMap { entity ->
maybeEmitEvent(BeforeConvertEvent<ENTITY>(entity, collection))
document.putAll(fields)
val update = Update()
fields
.filter { entry ->
!hashSetOf("_id", "createdAt", "createdBy", "modifiedAt", "modifiedBy").contains(entry.key)
}
.forEach { entry -> update.set(entry.key, entry.value) }
maybeEmitEvent(BeforeSaveEvent<ENTITY>(entity, document, collection))
template.updateFirst(query, update, collection)
}
.then(findById(id)).map { entity ->
maybeEmitEvent(AfterSaveEvent<ENTITY>(entity, document, collection))
entity
}
}
private fun <T> maybeEmitEvent(event: MongoMappingEvent<T>) {
eventPublisher?.publishEvent(event)
}
}
And the last part is to inform spring data.
#Configuration
#EnableReactiveMongoRepositories(
basePackages = ["**.repository"],
repositoryBaseClass = SimpleBaseGenericReactiveMongoRepository::class
)
class MongoConfiguration
Now you can use the interface as a base interface for your repository and have the functionality for your domain.
interface BookRepository : BaseMongoRepository<Book> {
findByNameContainingIgnoreCaseAndVisibileIsTrue(name:String): Flux<Book>
}
If you need a working example, you are welcome to check my medium:
https://medium.com/#ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9

JSF ManagedProperty not working for class

Sorry for my English. I want to set #ManagedProperty for class TaskBO, but it is not works. TaskMB class:
#ManagedBean(name="taskMB")
#RequestScoped
public class TaskMB implements Serializable {
#ManagedProperty(value="#{TaskBO}")
public TaskBO taskBO;
public TaskBO getTaskBO() {
return this.taskBO;
}
public void setTaskBO(TaskBO taskBO){
this.taskBO = taskBO;
}
//...
}
It prints the error:
javax.servlet.ServletException: Unable to set property taskBO for managed bean taskMB
javax.el.ELException: java.lang.IllegalArgumentException: Cannot convert com.otv.model.bo.TaskBO#6c80b8 of type class $Proxy135 to class com.otv.model.bo.TaskBO
But if I add interface ITaskBO, that it is works:
#ManagedProperty(value="#{TaskBO}")
public ITaskBO taskBO;
public ITaskBO getTaskBO() {
return this.taskBO;
}
public void setTaskBO(ITaskBO taskBO){
this.taskBO = taskBO;
}
Why not work #ManagedProperty with the class TaskBO?
Is best pratice wire interface instead of concrete class to prevent the problem you encountered.
Cannot convert com.otv.model.bo.TaskBO#6c80b8 of type class $Proxy135
to class com.otv.model.bo.TaskBO
Often Spring's managed object are proxied and a java proxy can be casted ONLY to interface and not to concrete class; the error above is generated because:
TaskBO object is managed by Spring and proxied to an object of type $Proxy135 (the real type of your object now is not really concrete class TaskBO but a proxy you can cast to ITaskBO, the $Proxy135)
you are trying to do some like public TaskBO taskBO = (TaskBO)$Proxy135; but cast a proxy to concrete class is impossible
The right way is to write public ITaskBO taskBO = (ITaskBO)$Proxy135; and this works because a proxy can be cast only to interface
Avoid - as much as possible - use of concrete class in favor of interface.
In addiction you can look here if you are mixing configuration how described in linked question.

Resources