How do i add a custom query to my Spring boot application and access it in the controller?
I have two tables called CarBrand and YearMade. CarBrand has ID, code and Brand as columns. YearMade also has ID, code and year as columns.
I have written my model classes with setter and getter methods for each entity. I have added my repository interfaces and my service classes.
public interface YearRepository extends JpaRepository<Year, Long> {
}
My Brand Repository
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
}
Here is my service class
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll(){
return yearRepository.findAll();
}
public void save(Year engineSize){
yearRepository.save(engineSize);
}
public Year get (long id){
return yearRepository.findById(id).get();
}
public void delete (Long id){
yearRepository.deleteById(id);
}
}
My Brand Service
public interface BService {
List<Brand> findVehicleBrand(String name);
}
And this.
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public List<Brand> findVehicleBrand(String name) {
var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
return brand;
}
}
In my controller, I get a path variable with a string, i use substring to break the string into two. The two substrings have the code for brand and year. The first two represent the year and the other three represent the brand. How do i compare the codes to the codes in the database to get the actual year and brand.
http://localhost:8081/vincode/wwQPT
The ww is the code for the year 1990 and QPT is for Honda Motor Company in the database.
I want a JSON response like this
{
Year Made : 1990,
Brand Name : Honda Motor Company
}
Here is the controller class i have so far.
#RequestMapping("/{vincode}")
public #ResponseBody String getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
System.out.println(yr);
return yr;
}
Where do i add the query and how do i use it in my controller?
Thank you.
If you dont have request mappong wlth value top of the class then http://localhost:8081/vincode/ww/QPT
RequestMapping("/vincode/{code}/{company}") can be more useful
There is no need to use substring maybe code or company key sizes changes.
Also service layer can be injected and used anytime.
Firstly add this statement in BrandRepository interface :
public interface BrandRepository extends JpaRepository<Brand, Long> {
#Query("select b from brand b where brand.brand = ?1")
List<Brand> findVehicleBrand(String brand);
public Brand findByCode(String code);
}
In YearRepository interface :
public interface YearRepository extends JpaRepository<Year, Long> {
public Year findByCode(String code);
}
Then add this method in BrandService Class:
public String findByCode (String code){
return brandRepository.findByCode(code).getBrand();
}
Then add this method in YearService Class:
public String findByCode (String code){
return yearRepository.findByCode(code).getYear;
}
Create Domain Class :
public class YearBrand
{
private String YearMade;
private String BrandName;
public YearBrand(String year, String brand)
{
this.YearMade=year;
this.BrandName=brand;
}
}
Then In Controller Class :
#RequestMapping("/{vincode}")
public YearBrand getAttr(#PathVariable(value="vincode") String vincode) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
return new YearBrand(yearService.findByCode(yr),brandService.findByCode(brand));
}
NOTES:
Make sure your Controller Class is annotated with #RestController
Spring Data JPA derives queries based on method naming conventions.
So, to get year by code in YearMade table, you need to modify your YearReporsitory interface like this (add an abstract method):
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
And, use this method in your YearService just as you've used other methods.
But, you cannot use the same method for getting brand by code requirement. You'll have to write a repo class for it like:
public interface BrandRepository extends JpaRepository<CarBrand, Long> {
public Integer findByCode(String code);
}
You can write these methods for all the members of your Entity class. You've to follow the naming convention to get Spring recognize it.
EDIT (to show how to use this in controller and service class):
YearRepository interface:
public interface YearRepository extends JpaRepository<Year, Long> {
// set return type as required
//find - Do What, ByCode - Criteria.
public Integer findByCode(String code);
}
BrandRepository
public interface BrandRepository extends JpaRepository<Brand, Long> {
/*The below two methods are abstract methods.*/
// it must follow the findby<MemberName> convention
//return CarBrand
CarBrand findByBrand(String brand);
/*return a CarBrand Entity*/
public CarBrand findByCode(String code);
YearService:
public class YearService {
#Autowired
private YearRepository yearRepository;
public List<Year> listAll() {
return yearRepository.findAll();
}
public void save(Year engineSize) {
yearRepository.save(engineSize);
}
public Year get(long id) {
return yearRepository.findById(id).get();
}
public void delete(Long id) {
yearRepository.deleteById(id);
}
public int getYearByCode(String code) {
//here, we're using this method just as you've used the methods above.
//Spring constructs the query at runtime
return yearRepository.findByCode(code); //<-- usage of the custom method
}
}
BService:
public interface BService {
CarBrand findVehicleBrand(String name);
}
BrandService:
#Service
#Transactional
public class BrandService implements BService{
#Autowired
private BrandRepository brandRepository;
public List<Brand> listAll(){
return brandRepository.findAll();
}
public void save(Brand brand){
brandRepository.save(brand);
}
public Brand get (long id){
return brandRepository.findById(id).get();
}
public void delete (Long id){
brandRepository.deleteById(id);
}
#Override
public CarBrand findVehicleBrand(String name) {
//var brand = (List<Brand>) brandRepository.findVehicleBrand(name);
var brand = brandRepository.findByBrand(name); //<-- using the custom method in brandRepository
return brand;
}
}
Your RepsonseDto:
class RepsonseDto {
private String yearMade;
private brandName;
//getters and setters
/*Use #JsonProperty("Year Made") and #JsonProperty("Brand Name") on your getters. Otherwise, you will get json reposnse as: "yearMade" and "brandName"*/
}
Controller:
There are better ways to write controllers and inject dependencies. Let's keep it simple for now.
#RequestController
class YourController {
//inject dependencies
#Autowired
YearService yearService;
#Autowired
BrandService brandService;
#RequestMapping("/{vincode}")
// the definition for ResponseEntity is above
public ResponseEntity<RepsonseDto> getAttr(#PathVariable(value="vincode") String vincode) {
// create a ReponseEntity object
RepsonseDto retEntity = new RepsonseDto();
// do a check for null and expected length of vincode
if(vincode != null && vincode.length() == 5) {
String yr = vincode.substring(0,1);
String brand = vincode.substring(2,4);
retEntity.setYearMade(yearService.getYearByCode(yr));
retEntity.setBrandName(brandService.findVehicleBrand(brand));
System.out.println(yr);
}
return new ResponseEntity<>(retEntity, HttpStatus.OK)
}
NOTE: I didn't use an IDE to write this. There may be compiler errors. Hope this gives you an idea of it all fits in.
Related
My MongoDB Schema look like this. I want to update quantity field using uname, prodname and quantity. I tried to write a function in CartService class but it shows error
"The method save(S) in the type CrudRepository<Cart,String> is not applicable for the arguments (Optional)
] with root cause"
Please suggest any other solution or point out my mistake in code.
"Id":"string",
"uname":"string",
"products":[
{
"prodname":"string",
"quantity":"int",
"price":"double"
}],
"tot_amt":"double",
}
This is one of my model Cart.java
public class Cart {
#Id
public String Id;
#Indexed(unique=true)
public String uname;
public List<Product>products;
public double tot_amt;
}
This is another model class Product.java
public class Product {
public String prodname;
public int quantity;
public double price;
}
This is the repository interface CartRepository.java
public interface CartRepository extends MongoRepository<Cart,String>{
#Query("{uname:?0}")
Optional<Cart> findByName(String name);
}
This is Service class
public class CartService {
#Autowired
public CartRepository cartRepo;
public MongoTemplate mt;
public void saveUser(Cart cart) {
List<Double>amt= new ArrayList<>();
List<Product>products=cart.getProducts();
products.forEach(p -> {
double price=p.getPrice();
int quantity=p.getQuantity();
amt.add(price*quantity);
});
double tot_amount = 0;
for (Double i : amt)
tot_amount += i;
cart.setTot_amt(tot_amount);
cartRepo.save(cart);
}
public List<Cart> getdata()
{
return cartRepo.findAll();
}
public Optional<Cart> getDetailsByName(String name) {
Optional<Cart> savedCartData=Optional.of(cartRepo.findByName(name).orElseThrow(()->new RuntimeException(String.format("Not found %s",name))));
return savedCartData;
}
public void updateProd(String name, String prodname, int qty) {
Optional<Cart> cart=cartRepo.findByName(name);
Cart c=cart.get();
List<Product>products=c.getProducts();
for(Product p:products)
{
if (p.getProdname().equals(prodname)) {
p.setQuantity(qty);
break;
}
}
c.setProducts(products);
cart=Optional.of(c);
cartRepo.save(cart);
}
And this is update function in controller class
#PutMapping("/{name}/{prodname}/{qty}")
public String updateProduct(#PathVariable String name,#PathVariable String prodname,#PathVariable
int qty)
{
cartService.updateProd(name,prodname,qty);
return "Product updated";
}
Ok, let me give you steps to achieve this, I won’t take the fun of coding away from you.
Get the cart using username (you already have the repository function for this)
Stream the products in the cart
filter the product which has the input product name.
Update the quantity in above stream (using peek) with the input quantity
Collect the product list from stream.
set the collected product list back to cart.
Save the cart save()
I have a Java database application using hibernate, with different Classes that have the same attributes (here: “active”).
In an interface, there is a function that retrieves entries from a database based on such an attribute (active).
So far, I was doing this:
//interface
public interface ObjSvcIntf {
default <Entity> ArrayList<Entity> get(Boolean active);
}
//implementation 1
public class ObjCarSvc implements ObjSvcIntf {
#SuppressWarnings("unchecked")
#Override
public ArrayList< ObjCar > get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + ObjCar.class.getSimpleName() + " where active = :active");
query.setParameter("active", active);
if (!query.list().isEmpty()) {
return (ArrayList< ObjCar >) query.list();
} else {
return null;
}
}
//implementation 1
public class ObjPersonSvc implements ObjSvcIntf {
#SuppressWarnings("unchecked")
#Override
public ArrayList< ObjPerson > get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + ObjPerson.class.getSimpleName() + " where active = :active");
query.setParameter("active ", active);
if (!query.list().isEmpty()) {
return (ArrayList< ObjPerson >) query.list();
} else {
return null;
}
}
As You can see, there is a lot of redundant code in each implementing class, which I would like to avoid.
What I would like instead therefore, is to have a generic default function in the interface, which will return the same for each implementation of the interface (unless overridden by the implementing class of course).
I.e., something like this (except this does not work, of course):
public interface ObjSvcIntf {
default <Entity> ArrayList<Entity> get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + Entity.class.getSimpleName() + " where active = :active");
query.setParameter("active", active);
return (ArrayList<Entity>) query.list();
}
}
I am lacking the proper understanding here, how to create the function in the interface in the right way, to be able to use it in different contexts/ different classes.
How can I adjust the function in the interface instead to make this happen?
public interface ObjSvcIntf<Entity> {
default <Entity> ArrayList<Entity> get(Boolean active) {
#SuppressWarnings("rawtypes")
Query query = DB.s.createQuery("from " + getImplClass().getSimpleName() + " where active = :active");
query.setParameter("active", active);
return (ArrayList<Entity>) query.list();
}
Class getImplClass();
}
And you could just provide that same class for each implementation as you provide for the generic type.
You can create an abstract function to return the object of the subclass. Something like this would work. Here is example code, where interface returns as list of object of the class implementing the interface.
public interface ObjSvcIntf<E> {
default List<E> get(Boolean active) {
var list = new ArrayList<E>();
list.add(self());
return list;
}
E self(); // function to return the sub class instance
}
I restructured the project to separate the Interface from its implementation.
Each class, extending the (abstract) implementation of the interface now sets an attribute of Type "Class" when calling the super constructor, with each function in the abstract class referring to that attribute.
Is there a better way?
What are potential problems with this approach?
Interface:
public interfaceObjSvcIntf {
<Entity> Object getById(Long id);
}
Implementing abstract Class:
public abstract class ObjSvcImpl implements ObjSvcIntf {
public Class<?> servicedClass;
// CONSTRUCTOR
public ObjSvcImpl(Class<?> servicedClass) {
this.servicedClass = servicedClass;
}
#Override
public <Entity> Object getById(Long id) {
return DB.getById(this.servicedClass, id);
}
}
Service Class:
public class ObjCarSvc extends ObjSvcImpl {
public ObjCarSvc() {
super(ObjCar.class);
}
}
Model Class:
#Entity
#Table(name = "OBJ_CAR")
public class ObjCar implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "OBJ_CAR_ID")
private Long objCarId;
#NotNull
#Column(name = "NAME")
private String name;
// Getters and Setters
}
I'm lazy person... i just want the simplest way, now i'm learning android room. This might be answered here "Why is it not possible to extend annotations in Java?", but it seems like to try pass annotation to another annotation, right? So, Could i pass annotation to the child class for example, here is my parent class 'Model':
#Entity
public abstract class Model implements Parcelable{
#Nullable
#PrimaryKey
private Object id;
public String toJSON(){
return new Gson().toJson(this);
}
public <T> T getId(Class<T>type) {
if(type.equals(Integer.class)||type.equals(int.class)) {
return (T) Integer.valueOf(new Double(Double.parseDouble(String.valueOf(id))).intValue());
}
else if(type.equals(Long.class)||type.equals(long.class)) {
return (T) Long.valueOf(new Double(Double.parseDouble(String.valueOf(id))).longValue());
}
else if(type.equals(Double.class)||type.equals(double.class)) {
return (T) Double.valueOf(id.toString());
}else if(type.equals(UUID.class)){
return (T) UUID.fromString(String.valueOf(id));
}
return type.cast(id);
}
public <T> void setId(T id) {
this.id = id;
}
}
I tried to pass #Entity to child class.
public class Movie extends Model {
public static final String IDENTIFIER = Movie.class.getSimpleName();
#SerializedName("popularity")
private double popularity;
#SerializedName("vote_count")
private long voteCount;
#SerializedName("video")
private boolean video;
}
Because my data access object cannot detect my 'Movie' class, except i'm added Entity annotation
#Dao
public interface FavMovieDAO {
#Query("SELECT * FROM Movie WHERE id = :movieId")
public Movie find (int movieId);
}
If there is no way, i'll declare my entire model class as an Entity... :/. Thanks in advance.
Recently I discovered ActiveObejcts and I really like it. Right now I'm using the last version from Atlassian plugin, only the net.java.ao part for ORM. Compiles and runs fine. Sure, I have to do some performance tests, if it fits my requirements.
There exists the #Implementation annotation. How is that used? The javadocs are very brief.
Update
Solution:
public class M5 {
public static void main(String[] args) throws SQLException {
EntityManager m = EntityManagerBuilder
.url("jdbc:hsqldb:./db/db")
.username("root")
.password("")
.c3po()
.build();
m.migrate(Employee.class);
Employee p = m.create(Employee.class);
p.setFirstName("Peter");
p.setLastName("Mmm");
System.err.println(p.getLastName()); // prints "ln: Mmm"
p.save();
}
}
public class Emp {
private Employee employee;
public Emp(Employee employee) {
this.employee = employee;
}
public String getLastName() {
return "ln: " + employee.getLastName();
}
}
#Implementation(Emp.class)
interface Employee extends Entity {
String getFirstName();
void setFirstName(String name);
String getLastName();
void setLastName(String name);
}
}
Active Objects takes your interface and, via reflection through a complicated proxy translates your getters, setters and relations into SQL statements. If you want some of your own code in there to add or modify functionality of your AO interface, you can use #Implementation
Example:
AO interface:
#Implementation(PersonImpl.class)
public interface Person extends Entity {
String getLastName();
void setLastName(String name);
String getFirstName();
void setFirstName(String name);
#Ignore
String getName();
}
Implementation:
public class PersonImpl {
private final Person person; // this will be the original entity proxy
public PersonImpl(Person person) {
this.person = person;
}
// "Implement" ignored functions
public String getName() {
return String.format("%s %s", this.person.getFirstName(), this.person.getLastName());
}
// "Enhance" AO getters/setters
public void setFirstName(String name) {
this.person.setFirstName("Foobar");
}
}
Note that AO accesses these implementation methods via reflection. They must match the names in the interface. This might lead to problems during refactorings, as method names might change and your compiler won't tell you that your corresponding impl method name hasn't.
I have found myself in the need to override a static method, simply because it makes most sense, but I also know this is not possible.
The superclass, Entity.java:
abstract public class Entity<T> {
public Entity() {
//set up database connection
}
abstract public static Map<Object, T> getAll();
abstract public void insert();
abstract public void update();
protected void getData(final String query) {
//get data via database
}
protected void executeQuery(final String query) {
//execute sql query on database
}
}
One of the many concrete implementations, Account.java:
public class Account extends Entity<Account> {
private final static String ALL_QUERY = "SELECT * FROM accounts";
private final static String INSERT_QUERY = "INSERT INTO accounts (username, password) VALUES(?, ?)";
private final static String UPDATE_QUERY = "UPDATE accounts SET password=? WHERE username=?";
private String username;
private String password;
public Account(final String username, final String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(final String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
#Override
public static Map<Object, Account> getAll() {
//return a map using the ALL_QUERY string, calls getData(string);
}
#Override
public void insert() {
//insert this using INSERT_QUERY, calls executeQuery(string);
}
#Override
public void update() {
//update this using UPDATE_QUERY, calls executeQuery(string);
}
}
I haven't been going in depth explaining the code, but any general feedback on it would also be appreciated, I hope the comments explain enough.
So basically I think we can all agree that using Account.getAll() makes more sense over new Account().getAll() (if I would introduce a dummy syntax for it).
However I do want to have it extend the Entity class, currently it is only for convienience, but later on I may have to use sets/lists/multisets of Entity and perform an update() action on all of them, for example if I would build some queue that performances all updates every minute.
So well, is there a way to construct getAll() correctly?
Regards.
You could have separate classes for operations on all elements:
abstract public class Collection<T extends Entity<T>> {
abstract public static List<T> getAll();
public void printAll() {
// Print all entries of List obtained from getAll()
}
}
Which you could use as:
public class Accounts extends Collection<Account> {
#Override
public List<Account> getAll() {
//return a list using the ALL_QUERY string, calls getData(string);
}
}
It doesn't seems to me that it is really "simply because it makes most sense".
Tying persistence at your entity is not a good idea. There are already lots of patterns that give an appropriate design on this problem.
For example, in Domain Driven Design, "Persistence Ignorance" is what people trying to achieve. Consider making a Repository for each of your entity:
interface Repository<T> {
List<T> findAll();
void insert(T);
void update(T);
}
so you can override it by whatever way you want:
interface UserRepository extends Repository<User> {
// some other methods which is meaningful for User
User findByLoginName(String loginName);
}
class UserRepositoryImpl implements UserRepository {
List<User> findAll() {
// call whatever query
}
void insert(T){...}
void update(T){...}
User findByLoginName(String loginName) {...}
}
With a proper design and a component to handle the retrieval/storage of entity, you can have a less-persistence-coupled entity, and with repository that can perform proper "overriding".