I have several application-x.properties files which are used for different profiles and each file contains a property with a specific value for each profile. I want to use this property in queries to database as a parameter.
Is it possible to add it using SpEL or something else?
For instance application.properties:
query.parameters.role: ADMIN
possible usage:
#Query(value = "select u from User u where u.role = :#{query.parameters.role}")
Set<User> getAllUsers();
You might do it by following way:
1.- Find all users by role using Repository Query Keywords
#Repository
public interface UserRepository extends JpaRepository<User, UUID> {
Set<User> findByRole(String role);
}
2.- Create a method called getAllUsers in UserService and get role value by using #Value:
#Service
public class UserService {
#Autowired
private UserRepository repository;
#Value("${query.parameters.role}")
private String role;
public Set<User> getAllUsers() {
return repository.findByRole(role);
}
}
Other way to answer to this question is implement a custom SpEL that is supported by #Query you can take a look this SpEL support in Spring Data JPA
Then you should follow these steps for your case:
1.- Create a ConfigProperties class so that you can read and get the application.properties.
#Configuration
#PropertySource("classpath:application.properties")
#ConfigurationProperties(prefix = "query")
public class ConfigProperties {
private Parameters parameters;
// standard getters and setters
}
public class Parameters {
private String role;
// standard getters and setters
}
2.- Implement a custom EvaluationContextExtensionSupport and reference to ConfigProperties
public class PropertyEvaluationContextExtension extends EvaluationContextExtensionSupport {
private final ConfigProperties configProperties;
public PropertyEvaluationContextExtension(final ConfigProperties configProperties) {
this.configProperties= configProperties;
}
#Override
public String getExtensionId() {
return "properties";
}
#Override
public ConfigProperties getRootObject() {
return this.configProperties;
}
}
3.- Create a bean in order to be called your custom PropertyEvaluationContextExtension
#Configuration
public class CustomConfig {
private final ConfigProperties configProperties;
public CustomConfig(final ConfigProperties configProperties) {
this.configProperties= configProperties;
}
#Bean
EvaluationContextExtensionSupport propertyExtension() {
return new PropertyEvaluationContextExtension(configProperties);
}
}
4.- Call the query.parameters.role by following format: ?#{query.parameters.role}
#Query(value = "SELECT u FROM User u WHERE u.role = ?#{query.parameters.role}")
Set<User> getAllUsers();
I'm using Spring Boot (1.3.3.RELEASE) and Hibernate JPA in my project. My entity looks like this:
#Data
#NoArgsConstructor
#Entity
#Table(name = "rule")
public class RuleVO {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 128, nullable = false, unique = true)
private String name;
#Column(name = "tag", length = 256)
private String tag;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleOutputArticleVO> outputArticles;
#OneToMany(mappedBy = "rule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RuleInputArticleVO> inputArticles;
}
My repository looks like this:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
}
In some cases I need to fetch only id and name attributes of entity RuleVO. How can I achieve this? I found a notice it should be doable using Criteria API and Projections but how? Many thanks in advance. Vojtech
UPDATE:
As has been pointed out to me, I'm lazy and this can very well be done hence I'm updating my answer after having looked around the web for a proper one.
Here's an example of how to get only the id's and only the names:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT r.id FROM RuleVo r where r.name = :name")
List<Long> findIdByName(#Param("name") String name);
#Query("SELECT r.name FROM RuleVo r where r.id = :id")
String findNameById(#Param("id") Long id);
}
Hopefully this update proves helpful
Old Answer:
Only retrieving the specific attributes name/id is not possible as this is not how spring was designed or any SQL database for that matter as you always select a row which is an entity.
What you CAN do is query over the variables in the entity, for instance:
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public RuleVo findOneByName(String name);
public RuleVo findOneByNameOrId(String name, Long id);
public List<RuleVo> findAllByName(String name);
// etc, depending on what you want
}
You can modify these however you want w.r.t. your needs. You can call these methods directly via the autowired repository
See http://docs.spring.io/spring-data/jpa/docs/current/reference/html/ Section 5.3 for more options and examples
interface IdOnly{
String getId();
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
public List<IdOnly> findAllByName(String name);
}
I notice that this is a very old post, but if someone is still looking for an answer, try this. It worked for me.
You can also define custom constructor to fetch specific columns using JPQL.
Example:
Replace {javaPackagePath} with complete java package path of the class
use as a constructor in JPQL.
public class RuleVO {
public RuleVO(Long id, String name) {
this.id = id;
this.name = name;
}
}
#Repository
public interface RuleRepository extends JpaRepository<RuleVO, Long> {
#Query("SELECT new {javaPackagePath}.RuleVO(r.id, r.name) FROM RuleVo r where r.name = :name")
List<RuleVO> findIdByName(#Param("name") String name);
}
Yes, you can achieve it with projections. You have many ways to apply them:
If you could upgrade to Spring Data Hopper, it provides an easy to use support for projections. See how to use them in the reference documentation.
Otherwise, first of all create a DTO with the attributes you want to load, something like:
package org.example;
public class RuleProjection {
private final Long id;
private final String name;
public RuleProjection(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
Of course, you could use Lombok annotations also.
Then, you can use in the JPQL queries like this:
select new org.example.RuleProjection(rule.id, rule.name) from RuleVO rule order by rule.name
Another option, if you want to avoid using DTO class names in your queries, is to implement your own query method using QueryDSL. With Spring Data JPA, you have to:
Create a new interface with the new method. Ex:
public interface RuleRepositoryCustom {
public List<RuleProjection> findAllWithProjection();
}
Change your repository to extend the new interface. Ex:
public interface RuleRepository extends JpaRepository<RuleVO, Long>, RuleRepositoryCustom {
...
Create an implementation of the Custom repository using the Spring Data JPA QueryDSL support. You have to previously generate the Q clases of QueryDSL, using its Maven plugin. Ex:
public class RuleRepositoryImpl {
public List<RuleProjection> findAllWithProjection() {
QRuleVO rule = QRuleVO.ruleVO;
JPQLQuery query = getQueryFrom(rule);
query.orderBy(rule.name.asc());
return query.list(ConstructorExpression.create(RuleProjection.class, rule.id, rule.name));
}
}
You can do it by using #Query annotation(HQL).
Please refer to the Spring docs below:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query
(search for #Query in spring document)
These are the following classes:
#Entity
public class Question {
#Id
public Long id;
public String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name = "OWNER_ID", referencedColumnName = "QUES_ID")
public List<Choice> choices = new ArrayList<>();
}
#Named
#Singleton
public interface QuestionRepository extends CrudRepository<Question , Long> {
Question findByName(String name);
}
And in the Controller file I have this following File
#Transactional
public Result getQuestion() {
List<Choices> list = this.questionRepository.findByName("name").choices;
list.size();
return ok();
}
list.size() in getQuestion() throws me a LazyInitializationException because there is not open sessions
I know that changing the fetch type to EAGER or using a JPQL query above the function definition in QuestionRepository might solve it, but there are part in my application where those wont help and I would require to lazy fetch.
How would make the entire code in getQuestion() function use a single session/transaction or even better my entire request to take place in an single session/transaction?
From Spring Data JPA reference documentation
4.7.1. Transactional query methods
To allow your query methods to be transactional simply use #Transactional at the repository interface
you define.
Example 100. Using #Transactional at query methods
#Transactional(readOnly = true)
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByLastname(String lastname);
#Modifying
#Transactional
#Query("delete from User u where u.active = false")
void deleteInactiveUsers();
}
Typically you will want the readOnly flag set to true as most of the query methods will only read data. In contrast to that deleteInactiveUsers() makes use of the #Modifying annotation and overrides the transaction configuration. Thus the method will be executed with readOnly flag set to false.
So just add #Transactional annotation to your repository interfaces.
I have an entity which would be stored in both relational(MySql) and graph database(Neo4j).
#Entity
#NodeEntity(partial = true)
public class User {
#NotNull
#Column(name = "UserName", unique = true)
private String userName;
#GraphProperty
String firstName;
}
I know we have JpaRepository and GraphRepository.
public interface UserRepository extends JpaRepository<User, Long> { }
public interface UserGraphRepository extends GraphRepository<User> { }
But is there any repository implementation for handling such a cross store entity? So I could do something like this.
public interface UserRepository extends CrossStoreRepository<User, Long> { }
So when I call save, it should save in both the databases.
I did some searching and found nothing.So started writing one myself.
If no such thing exist, is there a plan to add one in the future?
Is there any reason why you can't use spring-data-neo4j-cross-store as it is part of the spring-data-neo4j distribution?
I'm new to Neo4J and I have, probably an easy question.
There're NodeEntitys in my application, a property (name) is annotated with #Indexed(unique = true) to achieve the uniqueness like I do in JPA with #Column(unique = true).
My problem is, that when I persist an entity with a name that already exists in my graph, it works fine anyway.
But I expected some kind of exception here...?!
Here' s an overview over basic my code:
#NodeEntity
public abstract class BaseEntity implements Identifiable
{
#GraphId
private Long entityId;
...
}
public class Role extends BaseEntity
{
#Indexed(unique = true)
private String name;
...
}
public interface RoleRepository extends GraphRepository<Role>
{
Role findByName(String name);
}
#Service
public class RoleServiceImpl extends BaseEntityServiceImpl<Role> implements
{
private RoleRepository repository;
#Override
#Transactional
public T save(final T entity) {
return getRepository().save(entity);
}
}
And this is my test:
#Test
public void testNameUniqueIndex() {
final List<Role> roles = Lists.newLinkedList(service.findAll());
final String existingName = roles.get(0).getName();
Role newRole = new Role.Builder(existingName).build();
newRole = service.save(newRole);
}
That's the point where I expect something to go wrong!
How can I ensure the uniqueness of a property, without checking it for myself??
P.S.: I'm using neo4j 1.8.M07, spring-data-neo4j 2.1.0.BUILD-SNAPSHOT and Spring 3.1.2.RELEASE.
I walked into the same trap... as long as you create new entities, you will not see the exception - the last save()-action wins the battle.
Unfortunately, the DataIntegrityViolationException will be raised only in case of update an existing entity!
A detailed description of that behaviour can be found here:
http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#d5e1035
If you are using SDN 3.2.0+ use the failOnDuplicate attribute:
public class Role extends BaseEntity
{
#Indexed(unique = true, failOnDuplicate = true)
private String name;
...
}