How can i pass an string parameter to custom repository method? - ajax

I have a problem with AJAX + Rest + Spring boot + MySQL. Request is sucess but response is empty, no matter the format used in the request. In fact, the request payload shows ["customer" : "MyCustomer"] so problem is in controller, i think.
AJAX
function ajax_ciudad(customer) { // onchange en select box
$.ajax({
type: "POST",
contentType: "application/json",
url: "/api/ciudades",
data: JSON.stringify(customer),
dataType: 'json',
cache: false,
timeout: 600000,
}).then(function(data) {
DO SOMETHING....
});
});
}
CONTROLLER
#RestController
#RequestMapping
public class SearchController {
#Autowired
private MyRepository myRepository;
#PostMapping("/api/ciudades")
public #ResponseBody List<?> getSedes(String customer){
List<Canal> ciudades = myRepository.findBySede(customer);
return ciudades;
}
REPOSITORY
#Query(value ="SELECT * FROM canal WHERE cliente = ?1", nativeQuery = true)
List<Canal> findBySede(#Param("customer") String customer);
... Always return an empty object []
enter image description here
I have tried several formats for data, i tried getting the Select value using .val() to get the String, using .value to get the html value.... If i put manually a parameter like myRepository.findBySede("MyString") the ajax response has info, but when i pass the value it doesn't work; What i'm doing wrong? Thanks for your help
My entity class
#Entity
#Table(name = "canal")
public class Canal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "idcanal", unique = true, nullable = false)
int id;
#Column(name = "cliente")
String cliente;
#Column(name = "ciudad")
String ciudad;
#Column(name = "sede")
String sede;
#Column(name = "canal")
String canal;
#Column(name = "ip_pe")
String ip_pe;
#Column(name = "nombre_pe")
String nombre_pe;
#Column(name = "ipwan_pe")
String ipwan_pe;
#Column(name = "puerto_pe")
String puerto_pe ;
#Column(name = "ipwan_router")
String ipwan_router;
#Column(name = "enrutamiento")
String enrutamiento;
*****getters and setters****
}

I am hoping you have defined your repo correctly and returning correct data based on customer string.
Pass from ajax "customer" as #Param and Define your controller like this as #RestController includes #Controller and #ResponseBody:
#RestController
public class SearchController {
#Autowired
private MyRepository myRepository;
#PostMapping("/api/ciudades")
public List<Canal> getSedes(#Param("customer") String customer){
List<Canal> ciudades = myRepository.findBySede(customer);
return ciudades;
}
Or better way, return ResponseEntity..
#RestController
public class SearchController {
#Autowired
private MyRepository myRepository;
#PostMapping("/api/ciudades")
public ResponseEntity<?> getSedes(#Param("customer") String customer){
List<Canal> ciudades = myRepository.findBySede(customer);
return new ResponseEntity<>(ciudades , HttpStatus.OK);
}
Define your method like this in repo:
#Repository|
public interface MyRepository extends CrudRepository<Canal, String>
#Query(value ="SELECT * FROM canal GROUP BY cliente", nativeQuery = true)
List<Canal> findByIdcanal(); // works fines
//I assume here you have canal table and cliente is column in it as you
running native query
#Query(value ="SELECT * FROM canal WHERE cliente = ?1", nativeQuery = true)
List<Canal> findBySede(String customer); // doesn't work
//OR try this as JPQL if you have Canal is entity and cliente as string type in it.
#Query(value ="Select c from Canal c WHERE c.cliente = :cliente")
List<Canal> findBySede(#Param("cliente") String customer);
//OR simply have Named query I assume you have Canal and cliente string type
List<Canal> findByCliente(String customer);
}
UPDATE as I checked your native query is fine..
If you want to send customer(string) as #RequestBody in controller then follow below:
Create a Class Customer
public class Customer {
private String customer;
public String getCustomer() {
return customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
}
handle request as #RequestBody
#PostMapping("/api/ciudades")
public ResponseEntity<?> getSedes(#RequestBody Customer customer){
List<Canal> ciudades = myRepository.findBySede(customer.getCustomer());
return new ResponseEntity<>(ciudades , HttpStatus.OK);
}

Related

Eager fetch property in Mapped Super class

Our Mapped Super class has createdBy column which is defined to be lazily loaded
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractAuditingEntity implements Serializable {
#CreatedBy
#ManyToOne(fetch = FetchType.LAZY)
#XmlTransient
#JoinColumn(name = "created_by", updatable = false, columnDefinition = "bigint")
protected User createdBy;
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
I need to load this property eagerly in one of the sub class that inherits this aforementioned class.
#Override
#XmlElement(name = "createdBy")
#JsonProperty("createdBy")
public User getCreatedBy() {
return super.getCreatedBy();
}
How can I do that?
I tried the following (used NamedEntityGraph and HQL), but, both did not return createdBy from MappedSuperClass that is defined as lazy
//defined at the top of Model
#NamedEntityGraphs({
// eagerly fetches created by and program names when used
#NamedEntityGraph(
name = "graphWithCreatedBy",
attributeNodes = {
#NamedAttributeNode("createdBy")
}
)
})
//Repository method
#EntityGraph(value = "Program.createdBy", type = EntityGraph.EntityGraphType.FETCH) //tried both LOAD and FETCH
Program findOne(Specification<Program> specification);
---Using HQL FETCH JOIN --
//Repository Implementation
private static final String PROGRAM_USER_QUERY = "SELECT " +
" sp FROM Program sp " +
" LEFT JOIN FETCH sp.createdBy where sp.id = :id";
Query query = entityManager.createQuery(PROGRAM_USER_QUERY ).
setParameter("id", id);
query.getSingleResult();
Both approaches returns program, but not the createdBy User
What am I doing wrong?

How to send model property, the property is the model too in spring

I have two models.
#Entity
class Product {
#Id
private String id;
private String name;
#ManyToOne(optional = false)
#JoinColumn(name = "category_id", referencedColumnName = "id")
#NotNull(groups = {CREATE.class, UPDATE.class})
private Category category;
...
}
#Entity
class Category {
#Id
private String id;
private String name;
...
}
#RestController
#RequestMapping(path = "/product")
class ProductController {
#RequestMapping(method = RequestMethod.POST)
public void create(#ModelAttribute Product product) {
...
}
}
I want send request to ProductController:
http POST http://localhost:8080/product name=='Product 1' category=1
The param category is id of Category into db, but spring does not understand it.
Is it possible to do this?
Well, your entitiy classes are ok, but it's really weird to see parameters in the POST request especially in so sort as you have it placed here.
Here is my sample that is working properly
public class Product {
private String id;
private String name;
private Category category;
******
}
public class Category {
private String id;
private String name;
*******
}
#RestController
#RequestMapping(path = "/product")
public class ProductController {
#RequestMapping(method = RequestMethod.POST)
public void create(#ModelAttribute Product product) {
Product prd1 = product;
prd1.getId();
}
}
And just in case here is an appConfig:
#Configuration
#EnableWebMvc
public class AppConfig {
}
That is all. Now your contorller is expecting to get a message that is a Product instance.
Let's go onward. It's pretty weird to see parameters in the POST query. I've had some test and they are ok - just pass the data as a request body! Whatever you cose. For instance let's modify controller as it shown below:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public void create(#ModelAttribute Product product) {
Product prd1 = product;
prd1.getId();
}
}
And now you have to send a POST message with a body that contains a Product data in a JSON format, i.e
{ "id": 1 }
and it works for all other formats that are supported by spring

NamedQuery and no entity mapping

I would like to achieve the following. I have a query and I would like to run it and return rows in a REST call.
I do not want to map the query to a physical table, how would I achieve this?
I use Spring Boot 1.5.2.
After some try and fixes, I got the following solution.
Create a POJO class, no #Entity annotation. You want to add packageScan instructions if it is not found.
public class ActivityReport1 {
#Column
private BigInteger id;
#Column
private String title;
//Only getters
public ActivityReport1(BigInteger id,
String title){
this.id = id;
this.title = title;
}
In a class which is annotated with #Entity create the resultset mapping
#SqlResultSetMappings({
#SqlResultSetMapping(name = "ActivityReport1Mapping",
classes = {
#ConstructorResult(targetClass = ActivityReport1.class, columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "title")
})
})
})
Add repository class
#Repository
#Transactional
public class IActivityReport1Repository {
#PersistenceContext
private EntityManager entityManager;
public List<ActivityReport1> getResults(String userLogin) {
Query query = entityManager.createNativeQuery(
"SELECT " +
"t.request_id as id, t.request_title as title " +
"FROM some_table t ", "ActivityReport1Mapping");
List<ActivityReport1> results = query.getResultList();
return results;
}
}
And finally, the service impl class.
#Service
#Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class ActivityReport1ServiceImpl implements IActivityReport1Service {
private static final Logger _Logger = LoggerFactory.getLogger(ActivityReport1ServiceImpl.class);
#Autowired
private IActivityReport1Repository sessionFactory;
#Override
public List<ActivityReport1> runReport(String userLogin) {
List<ActivityReport1> reportRows = sessionFactory.getResults(userLogin);
return reportRows;
}
}
If you face with "Could not locate appropriate constructor", this means that on Java side it could not map db types to java types.
In my case I had to change id from Long to BigInteger and Timestamp to java.util.date.

Get URL parameter for crit use Spring MVC Hibernate

I want to be list out all my users with criteria of where id = formId. The code is working but just that it list out all the users instead of being filtered by formId. Please tell me where i did wrongly. Do tell me if you need any more info to solve this!
controller
*url = http://localhost:8080/User/Panda?Id=1
#RequestMapping(value = {"/{name}?Id={id}" }, method = RequestMethod.GET)
public String listClinicUser(ModelMap model, #PathVariable("id") Integer id) {
logger.info("Users List Page - Id = " + id);
List<User> user = service.findAllUsers(id);
model.addAttribute("users", user);
return "user/list";
}
Service
public List<User> findAllUsers(Integer id) {
return dao.findAllUsers(id);
}
DAO Class
public interface UserDao {
List<User> findAllUsers(Integer id);
}
*DAOImpl Class
#SuppressWarnings("unchecked")
public List<User> findAllUsers(Integer id) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("formId",id));
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List<User> users = (List<Usert>) crit.list();
return users;
}
*for createEntityCriteria() i created in another class call abstractDao and extends to it.
private final Class<T> persistentClass;
#SuppressWarnings("unchecked")
public AbstractDao(){
this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
}
protected Criteria createEntityCriteria(){
return getSession().createCriteria(persistentClass);
}
Class Entity
#Entity
#Table(name="USER")
public class User implements Serializable{
#NotEmpty
#Column(name="formId", nullable=false)
private Integer formId;
#NotEmpty
#Column(name="FIRST_NAME", nullable=false)
private String firstName;
#NotEmpty
#Column(name="LAST_NAME", nullable=false)
private String lastName;
public Integer getFormId() {
return formId;
}
public void setFormId(Integer formId) {
this.formId= formId;
}
...
}
value = {"/{name}?Id={id}" }
This is wrong way to extract URL param. If you want to get URL param, you should pass it to your method using #RequestParam annotation:
#RequestMapping(value = {"/{name}" }, method = RequestMethod.GET)
public String listClinicUser(ModelMap model, #RequestParam("Id") Integer id) {
//...
}
Spring automatically pass value that you need. For example in case of ?Id=1 Spring will pass 1 to your controller
In your url /{name} is a path variable and is annotated with #PathVariable like in:
#RequestMapping(value = "/foo/bar/{name}", method = GET)
#ResponseBody
public String getBarByName(#PathVariable String name) { ... }
And ?Id=id is a request parameter and is annotated wiht #RequestParam so if we map to url like this one:
http://localhost:8080/api/foo/bar?id=100
we do it like this
#RequestMapping(value = "/foo/bar", method = GET)
#ResponseBody
public String getBarById(#RequestParam("id") Integer id) { ... }
So to combine them to map to your url:
#RequestMapping(value = {"/{name}" }, params = "id", method = RequestMethod.GET)
public String listClinicUser(ModelMap model, #PathVariable String name, #RequestParam("id" Integer id)) { ... }

Enhanced Spring Data Rest delivers empty relations

in my current implementation using Spring-Boot, -HATEOAS, -Rest-Data I'm trying to spare some further rest calls and enhance my rest resource for credits to also deliver relations of a credit (see below account as ManyToOne and creditBookingClassPayments as OneToMany).
The problem now is that I'm not able to get it run. The call always delivers empty relations. I really would appreciate some help on this.
Here are the surroundings:
Credit.java
#Entity
#Getter
#Setter
public class Credit {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Setter(NONE)
#Column(name = "id")
private Long itemId;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="account_id", nullable = false)
private Account account;
#OneToMany(mappedBy = "credit")
private List<CreditBookingClassPayment> creditBookingClassPayments = new ArrayList<>();
#NotNull(message="Please enter a valid short name.")
#Column(length = 10, nullable = false)
private String shortName;
#NotNull(message="Please enter a valid name.")
#Column(nullable = false)
private String name;
...
}
CreditRepositoryCustomImpl.java
uses QueryDsl to enhance the credit resource with its realation
...
#Override
public List<Credit> findDistinctByAccountItemIdNew(Long accountId) {
QCredit credit = QCredit.credit;
QAccount account = QAccount.account;
QCreditBookingClassPayment creditBookingClassPayment = QCreditBookingClassPayment.creditBookingClassPayment;
QBookingClass bookingClass = QBookingClass.bookingClass;
BooleanExpression hasAccountItemId = credit.account.itemId.eq(accountId);
List<Credit> credits = from(credit).where(hasAccountItemId)
.innerJoin(credit.account, account)
.leftJoin(credit.creditBookingClassPayments, creditBookingClassPayment)
.leftJoin(creditBookingClassPayment.bookingClass, bookingClass).groupBy(credit.itemId).fetch();
return credits;
}
...
CreditController.java
looking into responseBody here all (account and credit payments) is available for credits
#RepositoryRestController
public class CreditController {
#Autowired
private CreditRepository creditRepository;
#RequestMapping(value = "/credit/search/findAllByAccountItemIdNew", method= RequestMethod.GET, produces = MediaTypes.HAL_JSON_VALUE)
#ResponseBody
public ResponseEntity<Resources<PersistentEntityResource>> findAllByAccountItemIdNew(#RequestParam Long accountId, PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
List<Credit> credits = creditRepository.findDistinctByAccountItemIdNew(accountId);
Resources<PersistentEntityResource> responseBody = new Resources<PersistentEntityResource>(credits.stream()
.map(persistentEntityResourceAssembler::toResource)
.collect(Collectors.toList()));
return ResponseEntity.ok(responseBody);
}
}
CreditResourceIntegrTest.java
here creditResourcesEntity hold the credit but account is null and creditBookingClassPayment is an empty array
#Test
public void testFindAllByAccountItemId() throws URISyntaxException {
URIBuilder builder = new URIBuilder(creditFindAllByAccountItemIdRestUrl);
builder.addParameter("accountId", String.valueOf(EXPECTED_ACCOUNT_ID));
builder.addParameter("projection", "base");
RequestEntity<Void> request = RequestEntity.get(builder.build())
.accept(MediaTypes.HAL_JSON).acceptCharset(Charset.forName("UTF-8")).build();
ResponseEntity<Resources<Resource<Credit>>> creditResourcesEntity =
restTemplate.exchange(request, new ParameterizedTypeReference<Resources<Resource<Credit>>>() {});
assertEquals(HttpStatus.OK, creditResourcesEntity.getStatusCode());
//assertEquals(EXPECTED_CREDIT_COUNT, creditResourcesEntity.getBody().getContent().size());
}
Do I miss something?
Thanks for your help!
Karsten
Okay, PersistentEntityResourceAssembler doesn't support relations. But this could be handled by using projections.
CreditProjection.java
#Projection(name = "base" , types = Credit.class)
public interface CreditProjection {
String getShortName();
String getName();
List<CreditBookingClassPaymentProjection> getCreditBookingClassPayments();
BigDecimal getValue();
BigDecimal getInterestRate();
BigDecimal getMonthlyRate();
}
CreditBookingClassPaymentProjection.java
#Projection(name = "base" , types = CreditBookingClassPayment.class)
public interface CreditBookingClassPaymentProjection {
BookingClass getBookingClass();
CreditPaymentType getCreditPaymentType();
}
CreditController.java
#RepositoryRestController
public class CreditController {
#Autowired
private ProjectionFactory projectionFactory;
#Autowired
private CreditRepository creditRepository;
#RequestMapping(value = "/credit/search/findAllByAccountItemIdNew", method = RequestMethod.GET, produces = MediaTypes.HAL_JSON_VALUE)
#ResponseBody
public ResponseEntity<Resources<?>> findAllByAccountItemIdNew(#RequestParam Long accountId,
PersistentEntityResourceAssembler persistentEntityResourceAssembler) {
List<Credit> credits = creditRepository.findDistinctByAccountItemIdNew(accountId);
List<PersistentEntityResource> creditResources = new ArrayList<>();
for (Credit credit : credits) {
// credit.getCreditBookingClassPayments()
PersistentEntityResource creditResource = persistentEntityResourceAssembler.toResource(credit);
creditResources.add(creditResource);
}
Resources<CreditProjection> responseBody = new Resources<CreditProjection>(credits.stream()
.map(credit -> projectionFactory.createProjection(CreditProjection.class, credit))
.collect(Collectors.toList()));
return ResponseEntity.ok(responseBody);
}
}

Resources