I'm working on a Spring app and defining various find methods on a repository:
#Repository
public interface TicketRepository extends JpaRepository<TicketEntity, Long> {
List<TicketEntity> findByTicketId(#Param("ticketId") Long ticketId);
List<TicketEntity> findByTicketIdAndState(#Param("ticketId") Long ticketId, #Param("state") String state);
List<TicketEntity> findByTicketIdAndStateAndFlagged(#Param("ticketId") Long ticketId, #Param("state") String state, #Param("flagged") String Flagged);
}
The problem is that I have 30 columns which can be optionally filtered on. This is will result in the repository methods becoming unwieldy:
List<TicketEntity> findByTicketIdAndStateAndFlaggedAndCol4AndCol5AndCol6AndCol7AndCol8AndCol9AndCol10AndCol11AndCol12AndCol13AndCol14AndCol15AndCol16AndCol17AndCol18AndCol19AndCol120....);
How should the JPA layer be designed to cater for this scenario ?
If I create an object with attributes:
public class SearchObject {
private String attribute1;
//Getter and Setters
.
.
.
.
}
Can I pass SearchObject into a a find method and Spring JPA will determine which attributes to insert AND statements for depending on which attributes are Null - if the attribute is not null a corresponding AND is generated for that attribute.
Create filter object that will contain all optional columns e.g.:
#AllArgsConstructor
public class TicketFilter {
private final String col1;
private final Integer col2;
public Optional<String> getCol1() {
return Optional.ofNullable(col1);
}
public Optional<Integer> getCol2() {
return Optional.ofNullable(col2);
}
}
Extend your Respoitory with JpaSpecificationExecutor
Create specification class:
public class TicketSpecification implements Specification {
private final TicketFilter ticketFilter;
public TicketSpecification(TicketFilter ticketFilter) {
this.ticketFilter = ticketFilter;
}
#Override
public Predicate toPredicate(Root<Ticket> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
ticketFilter.getTitle().ifPresent(col1 -> predicates.add(getCol1Predicate(root, col1)));
ticketFilter.getDescription().ifPresent(col2 -> predicates.add(getCol2Predicate(root, col2)));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
private Predicate getCol1Predicate(Root root, String title) {
return root.get("col1").in(col1);
}
}
Use your repository: ticketRepository.findAll(specification);
Use Spring Data JPA Specification
Detail Solution be patient
First create a SpecificationCriteria class to define your criterias means filtering column as key and filtering value as value
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class SpecificationCriteria {
private String key;
private Object value;
}
Then create SpecificationCriteriaBuilder to build your Criteria
#Service
public class SpecificationCriteriaBuilder {
public List<SpecificationCriteria> buildCriterias(String name) {
List<SpecificationCriteria> specificationCriterias = new ArrayList<SpecificationCriteria>();
if (!StringUtils.isEmpty(name)) {
specificationCriterias
.add(SpecificationCriteria.builder().key("name")
.value(name).build());
}
// Here you can add other filter one by one
return specificationCriterias;
}
}
Then create a SpecificationBuilder class to build your specifications.
You can build from the list of filter options(Criteria) to List of specification
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
#Service
public class SpecificationBuilder<T> {
public Specification<T> buildSpecification(List<SpecificationCriteria> specificationCriterias) {
if (ObjectUtils.isEmpty(specificationCriterias)) {
return null;
}
Specification<T> specification = getSpecification(specificationCriterias.get(0));
for (int index = 1; index < specificationCriterias.size(); index++) {
SpecificationCriteria specificationCriteria = specificationCriterias.get(index);
specification =
Specification.where(specification).and(getSpecification(specificationCriteria));
}
return specification;
}
public Specification<T> getSpecification(SpecificationCriteria specificationCriteria) {
Specification<T> specification = new Specification<T>() {
private static final long serialVersionUID = 2089704018494438143L;
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.equal(root.get(specificationCriteria.getKey()),
specificationCriteria.getValue());
}
};
return specification;
}
}
In service first build criteria and then build specification using them. Then use specifications in repository call
#Service
#Transactional
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class UserService {
private final SpecificationCriteriaBuilder criteriaBuilder;
private final SpecificationBuilder<User> specificationBuilder;
private final UserRepository userRepository;
public List<User> getAll(String name) {
List<SpecificationCriteria> specificationCriterias =
criteriaBuilder.buildCriterias(name); // here you can pass other parameter as function argument
Specification<User> specification =
specificationBuilder.buildSpecification(specificationCriterias);
List<User> users = userRepository.findAll(specification);// pass the specifications
return users;
}
Repository extend JpaSpecificationExecutor
#Repository
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
}
Related
My MongoDB Schema look like this. So I want to delete any one product using uname(i.e username) and prodname(i.e product name). Can we use #Query annotation to do this or any suggestions?
{
"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
#Repository
public interface CartRepository extends MongoRepository<Cart,String>{
#Query("{uname:?0}")
Optional<Cart> findByName(String name);
}
This is the 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;
}
If your repository interface looks like this:
public interface YourRepository extends MongoRepository<T, ID> {}
The "YourRepository" will have access to all these methods:
MongoRepository docs
Using #Query you are going to have to make custom queries, and indeed you can use it to perform the deletion you desire.
There is a cool article on Baeldung that can guide you to how you prefer to do it.
A guide to Queries in Spring Data MongoDB
I needed some custom QueryDSL enabled query methods and followed this SO answer.
That worked great, but after upgrading to Spring Boot 2.1 (which upgrades Spring Data), I've found that QuerydslJpaRepository has been deprecated.
Simply replacing it with QuerydslJpaPredicateExecutor - which the documentation tells me to use - leads to an error:
Caused by: java.lang.IllegalArgumentException: Object of class
[...ProjectingQueryDslJpaRepositoryImpl] must be an instance of
interface
org.springframework.data.jpa.repository.support.JpaRepositoryImplementation
...but implementing JpaRepositoryImplementation would mean that I have to implement all the standard CRUD methods, which I obviously don't want.
So if I remove the repositoryBaseClass config from #EnableJpaRepositories, to treat this just like a repository fragment with implementation, it will try to instantiate the fragment, even though it is marked with #NoRepositoryBean, giving me the error:
Caused by: java.lang.IllegalArgumentException: Failed to create query
for method public abstract java.util.Optional
ProjectingQueryDslJpaRepository.findOneProjectedBy(com.querydsl.core.types.Expression,com.querydsl.core.types.Predicate)!
At least 1 parameter(s) provided but only 0 parameter(s) present in
query.
...
Caused by: java.lang.IllegalArgumentException: At least 1 parameter(s)
provided but only 0 parameter(s) present in query.
Abriged version of source:
#Configuration
#EnableJpaRepositories(basePackageClasses = Application.class, repositoryBaseClass = ProjectingQueryDslJpaRepositoryImpl.class)
#EnableTransactionManagement
#EnableJpaAuditing
#RequiredArgsConstructor(onConstructor = #__({#Autowired}))
public class DatabaseConfig {}
_
#NoRepositoryBean
public interface ProjectingQueryDslJpaRepository<T> extends QuerydslBinderCustomizer<EntityPath<T>>, QuerydslPredicateExecutor<T> {
#NonNull
<P> Page<P> findPageProjectedBy(#NonNull Expression<P> factoryExpression, Predicate predicate,
#NonNull Pageable pageable);
#NonNull
<P> Optional<P> findOneProjectedBy(#NonNull Expression<P> factoryExpression, #NonNull Predicate predicate);
#Override
default void customize(#NonNull QuerydslBindings bindings, #NonNull EntityPath<T> root){
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
}
_
public class ProjectingQueryDslJpaRepositoryImpl<T, ID extends Serializable> extends QuerydslJpaRepository<T, ID>
implements ProjectingQueryDslJpaRepository<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final Querydsl querydsl;
public ProjectingQueryDslJpaRepositoryImpl(#NonNull JpaEntityInformation<T, ID> entityInformation, #NonNull EntityManager entityManager) {
this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
}
public ProjectingQueryDslJpaRepositoryImpl(#NonNull JpaEntityInformation<T, ID> entityInformation, #NonNull EntityManager entityManager,
#NonNull EntityPathResolver resolver) {
super(entityInformation, entityManager, resolver);
this.path = resolver.createPath(entityInformation.getJavaType());
PathBuilder<T> builder = new PathBuilder<>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
}
#Override
public <P> Page<P> findPageProjectedBy(#NonNull Expression<P> factoryExpression, Predicate predicate,
#NonNull Pageable pageable) {
final JPQLQuery<?> countQuery = createCountQuery(predicate);
JPQLQuery<P> query = querydsl.applyPagination(pageable, createQuery(predicate).select(factoryExpression));
return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
}
#Override
public <P> Optional<P> findOneProjectedBy(#NonNull Expression<P> factoryExpression, #NonNull Predicate predicate) {
try {
return Optional.ofNullable(createQuery(predicate).select(factoryExpression).from(path).fetchOne());
} catch (NonUniqueResultException ex) {
throw new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
}
}
With Spring Boot 2.1.1 the following solution may help you. The key is to extend JpaRepositoryFactory and override the method getRepositoryFragments(RepositoryMetadata metadata). In this method you can provide base (or more specific fragment) implementations for any custom repository which should be taken for every extending repository.
Let me show you an example:
QueryableReadRepository:
#NoRepositoryBean
public interface QueryableReadRepository<T> extends Repository<T, String> {
List<T> findAll(Predicate predicate);
List<T> findAll(Sort sort);
List<T> findAll(Predicate predicate, Sort sort);
List<T> findAll(OrderSpecifier<?>... orders);
List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
Page<T> findAll(Pageable page);
Page<T> findAll(Predicate predicate, Pageable page);
Optional<T> findOne(Predicate predicate);
boolean exists(Predicate predicate);
}
The following interface combines different repositories.
DataRepository:
#NoRepositoryBean
public interface DataRepository<T>
extends CrudRepository<T, String>, QueryableReadRepository<T> {
}
Now your specific domain repos can extend from DataRepository:
#Repository
public interface UserRepository extends DataRepository<UserEntity> {
}
QueryableReadRepositoryImpl:
#Transactional
public class QueryableReadRepositoryImpl<T> extends QuerydslJpaPredicateExecutor<T>
implements QueryableReadRepository<T> {
private static final EntityPathResolver resolver = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> builder;
private final Querydsl querydsl;
public QueryableReadRepositoryImpl(JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager, resolver, null);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
}
#Override
public Optional<T> findOne(Predicate predicate) {
return super.findOne(predicate);
}
#Override
public List<T> findAll(OrderSpecifier<?>... orders) {
return super.findAll(orders);
}
#Override
public List<T> findAll(Predicate predicate, Sort sort) {
return executeSorted(createQuery(predicate).select(path), sort);
}
#Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
return super.findAll(predicate, pageable);
}
#Override
public List<T> findAll(Predicate predicate) {
return super.findAll(predicate);
}
public List<T> findAll(Sort sort) {
return executeSorted(createQuery().select(path), sort);
}
#Override
public Page<T> findAll(Pageable pageable) {
final JPQLQuery<?> countQuery = createCountQuery();
JPQLQuery<T> query = querydsl.applyPagination(pageable, createQuery().select(path));
return PageableExecutionUtils.getPage(
query.distinct().fetch(),
pageable,
countQuery::fetchCount);
}
private List<T> executeSorted(JPQLQuery<T> query, Sort sort) {
return querydsl.applySorting(sort, query).distinct().fetch();
}
}
CustomRepositoryFactoryBean:
public class CustomRepositoryFactoryBean<T extends Repository<S, I>, S, I>
extends JpaRepositoryFactoryBean<T, S, I> {
public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager);
}
CustomRepositoryFactory:
public class CustomRepositoryFactory extends JpaRepositoryFactory {
private final EntityManager entityManager;
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
#Override
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
RepositoryFragments fragments = super.getRepositoryFragments(metadata);
if (QueryableReadRepository.class.isAssignableFrom(
metadata.getRepositoryInterface())) {
JpaEntityInformation<?, Serializable> entityInformation =
getEntityInformation(metadata.getDomainType());
Object queryableFragment = getTargetRepositoryViaReflection(
QueryableReadRepositoryImpl.class, entityInformation, entityManager);
fragments = fragments.append(RepositoryFragment.implemented(queryableFragment));
}
return fragments;
}
Main class:
#EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class)
public class App {
}
This has the advantage that you provide only one (fragment) implementation for a custom repo. The base repository implementation is still Spring's default implementation. The example provided a new repo but you can probably also just override the default implementation of QuerydslPredicateExecutor in CustomRepositoryFactory
In Spring Data JPA 2.1.6 the constructor of QuerydslJpaPredicateExecutor has changed.
I present here an alternative approach using a wrapper to https://stackoverflow.com/a/53960209/3351474. This makes the solution independent from the internals of Spring Data JPA. Three classes have to be implemented.
As an example I take here a customized Querydsl implementation using always the creationDate of an entity as sort criteria if nothing is passed. I assume in this example that this column exists in some #MappedSuperClass for all entities. Use generated static metadata in real life instead the hard coded string "creationDate".
As first the wrapped delegating all CustomQuerydslJpaRepositoryIml delegating all methods to the QuerydslJpaPredicateExecutor:
/**
* Customized Querydsl JPA repository to apply custom filtering and sorting logic.
*
*/
public class CustomQuerydslJpaRepositoryIml<T> implements QuerydslPredicateExecutor<T> {
private final QuerydslJpaPredicateExecutor querydslPredicateExecutor;
public CustomQuerydslJpaRepositoryIml(QuerydslJpaPredicateExecutor querydslPredicateExecutor) {
this.querydslPredicateExecutor = querydslPredicateExecutor;
}
private Sort applyDefaultOrder(Sort sort) {
if (sort.isUnsorted()) {
return Sort.by("creationDate").ascending();
}
return sort;
}
private Pageable applyDefaultOrder(Pageable pageable) {
if (pageable.getSort().isUnsorted()) {
Sort defaultSort = Sort.by(AuditableEntity_.CREATION_DATE).ascending();
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
}
return pageable;
}
#Override
public Optional<T> findOne(Predicate predicate) {
return querydslPredicateExecutor.findOne(predicate);
}
#Override
public List<T> findAll(Predicate predicate) {
return querydslPredicateExecutor.findAll(predicate);
}
#Override
public List<T> findAll(Predicate predicate, Sort sort) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(sort));
}
#Override
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(predicate, orders);
}
#Override
public List<T> findAll(OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(orders);
}
#Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(pageable));
}
#Override
public long count(Predicate predicate) {
return querydslPredicateExecutor.count(predicate);
}
#Override
public boolean exists(Predicate predicate) {
return querydslPredicateExecutor.exists(predicate);
}
}
Next the CustomJpaRepositoryFactory doing the magic and providing the Querydsl wrapper class instead of the default one. The default one is passed as parameter and wrapped.
/**
* Custom JpaRepositoryFactory allowing to support a custom QuerydslJpaRepository.
*
*/
public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {
/**
* Creates a new {#link JpaRepositoryFactory}.
*
* #param entityManager must not be {#literal null}
*/
public CustomJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
}
#Override
protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
final RepositoryComposition.RepositoryFragments[] modifiedFragments = {RepositoryComposition.RepositoryFragments.empty()};
RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);
// because QuerydslJpaPredicateExecutor is using som internal classes only a wrapper can be used.
fragments.stream().forEach(
f -> {
if (f.getImplementation().isPresent() &&
QuerydslJpaPredicateExecutor.class.isAssignableFrom(f.getImplementation().get().getClass())) {
modifiedFragments[0] = modifiedFragments[0].append(RepositoryFragment.implemented(
new CustomQuerydslJpaRepositoryIml((QuerydslJpaPredicateExecutor) f.getImplementation().get())));
} else {
modifiedFragments[0].append(f);
}
}
);
return modifiedFragments[0];
}
}
Finally the CustomJpaRepositoryFactoryBean. This must be registered with the Spring Boot application, to make Spring aware where to get the repository implementations from, e.g. with:
#SpringBootApplication
#EnableJpaRepositories(basePackages = "your.package",
repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class)
...
Here now the class:
public class CustomJpaRepositoryFactoryBean<T extends Repository<S, I>, S, I> extends JpaRepositoryFactoryBean<T, S, I> {
/**
* Creates a new {#link JpaRepositoryFactoryBean} for the given repository interface.
*
* #param repositoryInterface must not be {#literal null}.
*/
public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomJpaRepositoryFactory(entityManager);
}
}
This test case has a cleaner version of running queries using querydsl
https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/java/org/springframework/data/jpa/repository/support/QuerydslJpaPredicateExecutorUnitTests.java
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class,
em.getMetamodel());
SimpleJpaRepository<User, Integer> repository = new SimpleJpaRepository<>(information, em);
dave = repository.save(new User("Dave", "Matthews", "dave#matthews.com"));
carter = repository.save(new User("Carter", "Beauford", "carter#beauford.com"));
oliver = repository.save(new User("Oliver", "matthews", "oliver#matthews.com"));
adminRole = em.merge(new Role("admin"));
this.predicateExecutor = new QuerydslJpaPredicateExecutor<>(information, em, SimpleEntityPathResolver.INSTANCE, null);
BooleanExpression isCalledDave = user.firstname.eq("Dave");
BooleanExpression isBeauford = user.lastname.eq("Beauford");
List<User> result = predicateExecutor.findAll(isCalledDave.or(isBeauford));
assertThat(result).containsExactlyInAnyOrder(carter, dave);
Problem
I need to hash user password on entity level (not on Controller level) and #Converter seems to be right choice, with JavaEE, no spring.
Show me the code
Here the code using Jpa AttributeConverter:
import java.nio.charset.StandardCharsets;
import javax.inject.Inject;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.hash.Hashing;
#Converter
public class JPACryptoConverter implements AttributeConverter<String, String> {
private static Logger logger = LoggerFactory.getLogger(JPACryptoConverter.class);
private String salt = "helloWorld";
#Inject
private UserRepository userRepository;
#Override
public String convertToDatabaseColumn(String sensitive) {
return Hashing.sha512().hashString(salt + sensitive, StandardCharsets.UTF_8).toString();
}
#Override
public String convertToEntityAttribute(String sensitive) {
String tmp = Hashing.sha512().hashUnencodedChars(sensitive).toString();
return tmp.substring(0, salt.length());
}
}
I want to substitute salt string to entity defined salt on user table, but how to get this information ?
In order to get this information, I need to access userRepository using entity id and get salt, there is a way to find this information using #Converter ?
Note: I have tried with lifecycle listeners, preload, preupdate, prepersist, but because I'm using jpa Criteria, listeners are called after the query
I don't know exactly what you want, is that you want hash the user's pw before store into db?
You create a converter and want to using it?
something to do is add #Convert(converter = JPACryptoConverter.class)
#Entity
class UserEntity {
#Column(name = "pw")
#Convert(converter = JPACryptoConverter.class)
private String pw;
}
And please remove #Converter from your JPACryptoConverter.
It just:
public class JPACryptoConverter implements AttributeConverter<String, String>...
not:
#Converter
public class JPACryptoConverter implements AttributeConverter<String, String>...
//To using Strong pw hash
public static String hash(String plainPassword) {
if (StringUtils.isBlank(plainPassword)) {
throw new EmptyPasswordException("Password could not be empty");
}
return BCrypt.hashpw(plainPassword, BCrypt.gensalt());
}
public static boolean checkPassword(String plainPassword, String hash) {
return BCrypt.checkpw(plainPassword, hash);
}
If it`s for Authentication in SpringBoot then you should use a WebSecurityConfigurereAdapter and implement the configure method. and have something like this:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder());
}
private PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return charSequence.toString().equals(s);
}
};
}
I have a class like below:
package com.company.data.render.model
#RestController
public class ControllerClass {
#Autowired
ApplicationPropertiesServiceImpl services;
#RequestMapping(value = "/node1", method = RequestMethod.GET)
#ResponseBody
public ParentNode getNode1()
{
Child node = new Child();
List<Map<String, Object>> properties properties = services.getData("A",xxx);
node.addtree();
node.setProperties(properties);
return node;
}
} -------------------------------------------------------------------------------
package com.company.data.service;
#Component
public List<Map<String, Object>> getData(String type,String name)
{
if(type.equalsIgnoreCase("A"))
{
String sql = "select * from data.data_properties(?)";
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql,host);
return rows;
}else if(properties.equalsIgnoreCase("B"))
{
String sql = "select * from data.application_properties(?)";
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql,host);
return rows;
}
}
-------------------------------------------------------------------------------
package com.company.data.render.model;
#Component
public class Child {
#Autowired
ApplicationPropertiesServiceImpl services;
public void addtree()
{
List<Map<String, Object>> properties=services.getData("B", "xxy");
}
}
How can I access the getdata() function in Child class.I am getting null pointer exception for service object though I have autowired the ApplicationPropertiesServiceImpl
Seems like you are going to have 2 Controllers.
Clearly your controller is doing too much. And it's not a good idea to inject a controller into another controller.
I suggest you to make a Service and a Repository:
The model is receiving to much data from the Controller so I suggest to create a class to make it more clear, because return a Map is too abstract and make the code hard to read.
public class CarProperties {
private Integer id;
private String name;
private Integer age;
private String color;
//setters and gettters
....
}
Service:
public interface CarPropertiesService {
public List<CarProperties> findAll(String type);
}
#Service("CarPropertiesService")
public class CarPropertiesServiceImpl implements CarPropertiesService {
#Autowired
private CarPropertiesDAO carPropertiesDAO;
public List<CarProperties> findAll(String type) {
List<CarProperties> result = new ArrayList<>();
if ("XXX".equalsIgnoreCase(type)) {
List<Map<String, Object>> carPropertiesList = carPropertiesDAO.findAll();
for(Map<String, Object> carProperties : carPropertiesList) {
result.add(getCarPropertiesInstance(carProperties));
}
}
return result;
}
private CarProperties getCarPropertiesInstance(Map<String, Object> properties) {
CarProperties instance = new CarProperties();
instance.setId(properties.get("id"));
instance.setName(properties.get("name"));
...
return instance;
}
}
DAO:
public interface CarPropertiesDAO {
public List<Map<String, Object>> findAll();
}
#Repository("CarPropertiesDAO")
public class CarPropertiesDAOImpl implements CarPropertiesDAO {
...
public List<Map<String, Object>> findAll() {
String sql = "select * from data.car_properties(?)";
return jdbcTemplate.queryForList(sql,host);
}
}
and finally your controllers would make usage of the service:
#RestController
public class ControllerClass {
#Autowired
private CarPropertiesService carPropertiesService;
#RequestMapping(value = "/node1", method = RequestMethod.GET)
#ResponseBody
public ParentNode getNode1()
{
List<CarProperties> properties = carPropertiesService.findAllByType("XXX");
return properties;
}
#RequestMapping(value = "/prop/{type}/{name}", method = RequestMethod.GET)
#ResponseBody
public List<CarProperties> getData(#PathVariable String type,#PathVariable String name)
{
List<CarProperties> rows = carPropertiesService.findAllByType(type);
...
}
}
#Controller
public class Controller2 {
#Autowired
CarPropertiesService carPropertiesService;
public void addtree(){
List<CarProperties> rows = carPropertiesService.findAllByType("XXX");
}
}
Resuming: Controllers should not worry about the business, but only returning the data. Service should be where the business is, like calculations, data exchange, etc... The DAO class you can clearly see it is used to DB actions. Also keep in mind the access to the DAO objects should be in Services/Facedes, using it in the Controller recommended. So using this structure your code would became more reusable and easy to maintain.
Pls try this :-
#Component
#ComponentScan(basePackages="<provide your base pkg where it will scan for the component ControllerClass>")
#ConditionalOnBean(ControllerClass.class)
public class Child {
#Autowired(required=true)
private ControllerClass controllerClass;
public void addtree(){
controllerClass.getData("XXX",xxx)
}
}
I have an entity class A which has a Set of entities of class B with a ManyToMany relationship (out of scope why I need this)
class A {
#ManyToMany(cascade = CascadeType.ALL)
Set<B> setOfB;
}
Now, given an object of class B, how can I retrieve the object(s) of class A which has the B object in its Set??
I have tried in my class A repository with this:
interface Arepository extends JpaRepository<A, Long> {
#Query("from A a where ?1 in a.setOfB")
List<A> findByB(B b)
}
But it gives me a SQLGrammarException, so which is the correct syntax?
Thank you for your help.
Try with #Query("SELECT a from A a where ?1 member of a.setOfB").
Metamodel class:
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(A.class)
public class A_ {
public static volatile SetAttribute<A, B> bSet;
}
Specification utils:
public class ASpecs {
public static Specification<A> containsB(B b) {
return (root, query, cb) -> {
Expression<Set<B>> bSet = root.get(A_.bSet);
return cb.isMember(b, bSet);
};
}
}
Your repository:
public interface ARepository extends JpaRepository<A, Long>, JpaSpecificationExecutor<A> {
}
Usage:
#Service
public class YourService {
#Resource
private ARepository repository;
public List<A> getByB(B b) {
return repository.findAll(ASpecs.containsB(b));
}
}