A Schedule entity has a one to one relationship with a Market entity as well as some other "simple" properties.
Here is my ScheduleRepository:
#RepositoryRestResource(path = "schedule")
public interface ScheduleRepository extends JpaRepository<Schedule, Long>
{
Collection<Schedule> findByMarket(Market market);
}
"findByMarket" method works fine when invoking the method programmatically. However, when invoking directly from a web application (http://localhost:8080/schedule/search/findByMarket), the request type must be GET.
My question is how do I pass a Market JSON object using GET? Using POST wouldn't be an issues but findXxx methods must use GET. I tried passing something like:
?market={marketId:60}
in the querystring but to no avail.
Not knowing what your controller looks like, I would assume that if you wanted to pass marketId on a get it would look like.
?marketId=60
And your method would look like. The method you use will handle converting to and from JSON.
#Get
#Path("/query")
#Produces({"application/xml", "application/json"})
public Todo whatEverNameYouLike(#PathParam("marketId") String marketId)
In the documentation it is referenced to use the #Param annotation, so you can call to your rest service giving a query parameter. Here you have an example:
package hello;
import java.util.List;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
I ended up using a #Query annotation, like jdepedro suggested:
#Query("select s from Schedule s where s.market.marketId = :marketId and s.locale.localeId = :localeId and s.offline >= :offline order by s.placement, s.slot, s.online")
Collection<Schedule> findByMarket(#Param("marketId") Integer marketId, #Param("localeId") Integer localeId, #Param("offline") Date offline);
Related
I try to create query setAll , because a entity with top isValid and I want all value to false before I use saveAll for udpade my database. I use this methods synchronize my database with a batch 6000-7000 lines actually.
import org.springframework.data.repository.CrudRepository;
public interface DomRepository extends CrudRepository<Dom, String> {
public Domaine findDomByName(String dom);
public List<Dom> findAll();
public void setIsValidAll(boolean isValid);
}
import javax.persistence.*;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.data.annotation.CreatedDate;
#Entity
#DynamicUpdate
public class Dom{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
// more attribute
private boolean isValid;
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'setIsValidAll' found for type 'Dom'!
It's not entirely clear what you want to achieve based on the description you have provided but if I get this right, you would like to create a repository when that will effectively update the value of the isValid field for all Dom objects. If that is indeed the case then you could simply create a custom repository method that will accept your flag and will make use of a simple HQL or JPQL to update all records in the database. For example something like this could work for this case:
#Modifying
#Query("UPDATE Dom SET isValid = :valid")
void updateValidAll(boolean valid);
If on the other hand you want to perform the update in memory on the fetched entities and then perform a saveAll you could do something like this:
#Transactional
public void updateAllRecords2(boolean isValid) {
var records = repository.findAll()
.stream()
.peek(dom -> dom.setValid(isValid))
.toList();
repository.saveAll(records);
}
Note that the latter is not very optimal as it uses peek which should not be used on operations that have side-effects (i.e mutations). On top of that will will perform an update query for every single entity that is present, meaning N queries for N entities.
I'm working on a RESTful Spring Boot project that fetches data from MySQL DB.
I want to print just all categories that have active field equal to 1
and I want to apply that for all method in CategoryRepository Class: findAll, findByParentId ..etc.
package com.userService.repositories;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.userService.entities.Category;
public interface CategoryRepo extends JpaRepository<Category, Integer> {
#Query("where active =1")
public List<Category> findByParentId(int id);
}
I tried to use the query method but it doesn't work and gave me an exception
If you are using Hibernate as your persistence provider, you can take advantage of #Where clause on the entity level:
#Where(clause = "active =1")
#Entity
public class Category{
This will be applied to all queries that go through the persistence provider.
it could be helpful to you if you are using the query method you should specify
select alias_name from Category c where condition
or
from Category where condition
or
directly by using method
findByActive(int id);
public interface CategoryRepo extends JpaRepository<Category, Integer> {
#Query("select c from Category c where c.active =1")
public List<Category> findByParentId(int id);
}
#Query annotation allows to execute native queries. So, I think, you should specify the complete sql as follows
#Query("select c from Category c where c.active =1")
Normally I use annotiations:#Query("SELECT c FROM Country c") with JpaRepositoryor predefined methods like findAll
but in my case I want to generate dynamic query.
String baseQuery =SELECT c FROM Country c`
if(age!=null)
baseQuery+="WHERE c.age=20"
I need to perform same query from code level like this:
Query q1 = em.createQuery("SELECT c FROM Country c");
but I dont use EntityManager in spring boot
How can I generate query from code level?
If you would like to create dynamic queries from code you can take advantage of Spring's JdbcTemplate. Using spring boot it is as simple as injecting JdbcOperations bean to your repository class (assuming you have provided spring-boot-starter-jdbc module to your project).
But remember! This solution uses SQL, not JPQL. That's why you have to use proper tables and columns names in queries and properly map result to objects (i.e. using RowMapper)
This simple example worked fine for me (with different entity, but in same manner - I've adapted it to your example):
#Repository
public class CountryRepository {
#Autowired
private JdbcOperations jdbcOperations;
private static String BASIC_QUERY = "SELECT * FROM COUNTRY";
public List<Country> selectCoutry(Long age){
String query = BASIC_QUERY;
if (age != null){
query += " WHERE AGE = ";
query += age.toString();
}
//let's pretend that Country has constructor Conutry(String name, int age)
return jdbcOperations.query(query, (rs, rowNum) ->
{ return new Country(rs.getString("NAME"), rs.getInt("AGE");}
);
};
}
Then in service or whatever you inject CountryRepository and call method.
Since you're using Spring Boot, you can use Spring Data to create queries in your repository:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
}
Not a 100% on syntax, but should be something similar.
Now you can autowire this class:
#Autowired
public CountryRepository countryRepo;
And all basic methods are already available to you like:
countryRepo.findOne(id);
countryRepo.find();
If you want to make more advanced queries, you can use Spring Data e.g.:
#Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
public Country findByNameAndContinent(String name, String continent);
}
This is just an example (a stupid one) of course and assumes your Country class has the field names 'name' and 'continent' and both are strings. More is available here:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Section 5.3 more specifically.
PS: Make sure your Country class has the #Entity annotation
Hi I am using elastic search Spring data. Domain structure of my project keeps on changing.So I have to drop the index in order to change the mapping every time. To overcome this problem, I am using Aliases.
I created an Alias using:
elasticsearchTemplate.createIndex(Test.class);
elasticsearchTemplate.putMapping(Test.class);
String aliasName = "test-alias";
AliasQuery aliasQuery = new AliasBuilder()
.withIndexName("test")
.withAliasName(aliasName).build();
elasticsearchTemplate.addAlias(aliasQuery);
I have a test class:
import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
import org.springframework.data.elasticsearch.annotations.FieldIndex
import org.springframework.data.elasticsearch.annotations.FieldType
import org.springframework.data.elasticsearch.annotations.Setting
#Document(indexName = "test", type = "test")
#Setting(settingPath = 'elasticSearchSettings/analyzer.json')
class Test extends BaseEntity{
#Id
#Field(type = FieldType.String, index = FieldIndex.not_analyzed)
String id
#Field(type = FieldType.String, index = FieldIndex.analyzed, indexAnalyzer = "generic_analyzer", searchAnalyzer = "generic_analyzer")
String firstName
}
TestRepository Class:
package com.as.core.repositories
import com.as.core.entities.Test
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository
interface TestRepository extends ElasticsearchRepository<Test, String>
{
}
My question is how can I read from alias instead of the index itself?
Does write operation also takes place on alias.
I have looked at following link:
https://www.elastic.co/guide/en/elasticsearch/guide/current/index-aliases.html#index-aliases
It says that we will have to interact the alias instead of the actual index.How to achieve this using Elasticsearch Spring data Java API.
I have worked around this limitation by using the ElasticsearchTemplate in the repository class associated with the object (although it would be much nicer if there was a way to specify an alias name on the entity itself).
The way it works is to create a custom repository interface. In your case it would be TestRepositoryCustom:
public interface TestRepositoryCustom
{
Test> findByCustom(...);
}
Then implement this interface appending 'Impl' to the end of the base repository name:
public class TestRepositoryImpl implements TestRepositoryCustom
{
Page<Test> findByCustom(Pageable pageable, ...)
{
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
FilterBuilder filter = FilterBuilders.staticMethodsToBuildFilters;
/*
* Your code here to setup your query
*/
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder().withQuery(boolQuery).withFilter(filter).withPageable(pageable);
//These two are the crucial elements that will allow the search to look up based on alias
builder.withIndices("test-alias");
builder.withTypes("test");
//Execute the query
SearchQuery searchQuery = builder.build();
return elasticSearchTemplate.queryForPage(searchQuery, Test.class);
}
}
Finally in your base JPA repsitory interface, TestRepository, extend the TestRepositoryCustom interface to get access to any methods on your custom interface from your repository bean.
public interface TestRepository extends ElasticsearchRepository<Consultant, String>, TestRepositoryCustom
{
}
What I would really like to see is an annotation on the entity like:
#Document(aliasName="test-alias")
This would just work in the background to provide searching on this index out of the gate so that all the jpa queries would just work regardless of the index name.
Spring-data-elasticsearch supports finding documents in an alias. We were able to make version 3.2.6.RELEASE read documents annotated with
#Document(
indexName = "alias",
createIndex = false,
type = '_doc'
)
from a Spring Data ElasticsearchRepository backed by a ElasticsearchRestTemplate
I followed this example, which allows to post a unique Person object. I want a REST service where I can post a collection of Person at once, e.g. a list/any collection named Team with numerous Person objects in just one call.
I mean, my question is not exactly about the OneToMany relationship, where you send each person in a REST request. This topic is well answered.
I want to send a collection of Person objects taking advantage of #RepositoryRestResource or another feature from Spring Data Rest. Is this possible with Spring Data Rest or should I workaround by creating a controller, receive the list and parse the Team list to insert each Person?
I found this feature request, which seems to answer that nowadays Spring Rest Data is missing what I am looking for, but I am not sure.
In my business requirement, application A will post a list of orders to application B and I have to save it in database for future processing, so, after reading about Spring Data Rest and making some samples, I found its clean architecture amazing and very suitable for my requirement except for the fact that I didn't figure out how to post a list.
Well, AFAIK you can't do that with spring data rest, just read the docs and you will see, that there is no mention about posting a list to collection resource.
The reason for this is unclear to me, but for one thing - the REST itself doesn't really specify how you should do batch operations.
So it's unclear how one should approach that feature, like should you POST a list to collection resource? Or should you export resource like /someentity/batch that would be able to patch, remove and add entities in one batch? If you will add list how should you return ids? For single POST to collection spring-data-rest return id in Location header. For batch add this cannot be done.
That doesn't justify that spring-data-rest is missing batch operations. They should implement this IMHO, but at least it can help to understand why are they missing it maybe.
What I can say though is that you can always add your own Controller to the project that would handle /someentity/batch properly and you can even probably make a library out of that, so that you can use it in another projects. Or even fork spring-data-rest and add this feature. Although I tried to understand how it works and failed so far.
But you probably know all that, right?
There is a feature request for this.
Based on user1685095 answer, You can make custom Controller PersonRestController and expose post collection of Person as it seem not exposed yet by Spring-date-rest
#RepositoryRestController
#RequestMapping(value = "/persons")
public class PersonRestController {
private final PersonRepository repo;
#Autowired
public AppointmentRestController(PersonRepository repo) {
this.repo = repo;
}
#RequestMapping(method = RequestMethod.POST, value = "/batch", consumes = "application/json", produces = "application/json")
public #ResponseBody ResponseEntity<?> savePersonList(#RequestBody Resource<PersonWrapper<Person>> personWrapper,
PersistentEntityResourceAssembler assembler) {
Resources<Person> resources = new Resources<Person>(repo.save(personWrapper.getContent()));
//TODO add extra links `assembler`
return ResponseEntity.ok(resources);
}
}
PersonWrapper to fix:
Can not deserialize instance of org.springframework.hateoas.Resources out of START_ARRAY token\n at [Source: java.io.PushbackInputStream#3298b722; line: 1, column: 1]
Update
public class PersonWrapper{
private List<Person> content;
public List<Person> getContent(){
return content;
}
public void setContent(List<Person> content){
this.content = content;
}
}
public class Person{
private String name;
private String email;
// Other fields
// GETTER & SETTER
}
I tried to use #RequestBody List<Resource<MyPojo>>.
When the request body does not contain any links, it works well, but
if the element carries a link, the server could not deserialize the request body.
Then I tried to use #RequestBody Resources<MyPojo>, but I could not figure out the default name of a list.
Finally, I tried a wrapper which contained List<Resource<MyPojo>>, and it works.
Here is my solution:
First create a wrapper class for List<Resource<MyPojo>>:
public class Bulk<T> {
private List<Resource<T>> bulk;
// getter and setter
}
Then use #RequestBody Resource<Bulk<MyPojo>> for parameters.
Finally, example json with links for create bulk data in one request:
{
"bulk": [
{
"title": "Spring in Action",
"author": "http://localhost:8080/authors/1"
},
{
"title": "Spring Quick Start",
"author": "http://localhost:8080/authors/2"
}
]
}
#RequestMapping(method=RequestMethod.POST, value="/batchInsert", consumes = "application/json", produces = "application/json")
#ResponseBody
public ResponseEntity<?> batchInsert(#RequestBody Resources<Person> people, PersistentEntityResourceAssembler assembler) throws Exception {
Iterable<Person> s = repo.save( people.getContent() ); // save entities
List<PersistentEntityResource> list = new ArrayList<PersistentEntityResource>();
Iterator<Sample> itr = s.iterator();
while(itr.hasNext()) {
list.add( assembler.toFullResource( itr.next() ) );
}
return ResponseEntity.ok( new Resources<PersistentEntityResource>(list) );
}
Base the answer of totran, this is my code:
There are dependencies:
springBootVersion = '2.4.2'
springDependencyManagement = '1.0.10.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
The codes:
import icu.kyakya.rest.jpa.model.Address;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
#RepositoryRestResource(collectionResourceRel = "address", path = "address")
public interface AddressRepository extends PagingAndSortingRepository<Address, Long> {
//...
}
import lombok.Data;
import java.util.List;
#Data
public class Bulk<T> {
private List<T> bulk;
}
import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.ExposesResourceFor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
#BasePathAwareController // if base url exists, it needs to be added
#RepositoryRestController
#RequiredArgsConstructor
#ExposesResourceFor(Address.class)
public class AddressController {
private final AddressRepository repo;
#PostMapping("/address/saveAll")
public ResponseEntity<Iterable<Address>> saveAll(#RequestBody EntityModel<Bulk<Address>> bulk) {
List<Address> addresses = Objects.requireNonNull(bulk.getContent()).getBulk();
Iterable<Address> resp = repo.saveAll(addresses);
return new ResponseEntity<>(resp,HttpStatus.CREATED);
}
}
The way more like Spring data rest:
import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.ExposesResourceFor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
#BasePathAwareController // if base url exists, it needs to be added
#RepositoryRestController
#RequiredArgsConstructor
#ExposesResourceFor(Address.class)
public class AddressController {
private final AddressRepository repo;
private final RepositoryEntityLinks entityLinks; //get link
/**
* curl -i -X POST -H "Content-Type:application/json" -d '{ "bulk": [ {"country" : "Japan" , "city" : "Tokyo" }, {"country" : "Japan" , "city" : "Osaka" }]} ' http://localhost:8080/api/v1/address/saveAll
*
* #return 201 https://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.default-status-codes
*/
#PostMapping("/address/saveAll")
public ResponseEntity<CollectionModel<EntityModel<Address>>> List<Address> data = Objects.requireNonNull(bulk.getContent()).getBulk();
Iterable<Address> addresses = repo.saveAll(data);
ArrayList<EntityModel<Address>> models = new ArrayList<>();
addresses.forEach(i->{
Link link = entityLinks.linkToItemResource(Address.class, i.getId()).withRel("self");
models.add(EntityModel.of(i).add(link));
});
return new ResponseEntity<>(CollectionModel.of(models),HttpStatus.CREATED);
}
}