Creating a custom query with Spring DATA JPA? - mysql

I'm working on a project with Spring Data JPA. I have a table in the database as my_query.
I want to create a method which takes a string as a parameter, and then execute it as a query in the database.
Method:
executeMyQuery(queryString)
As example, when I pass
queryString= "SELECT * FROM my_query"
then it should run that query in DB level.
The repository class is as follows.
public interface MyQueryRepository extends JpaRepository<MyQuery, Long>{
public MyQuery findById(long id);
#Modifying(clearAutomatically = true)
#Transactional
#Query(value = "?1", nativeQuery = true)
public void executeMyQuery(String query);
}
However, it didn't work as I expected. It gives the following error.
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''select * from my_query;'' at line 1
Is there any other way, that I could achieve this goal?

The only part of it you can parameterise are values used in WHERE clause. Consider this sample from official doc:
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}

Using EntityManager you can achieve this .
Suppose your entity class is like bellow:
import javax.persistence.*;
import java.math.BigDecimal;
#Entity
#Table(name = "USER_INFO_TEST")
public class UserInfoTest {
private int id;
private String name;
private String rollNo;
public UserInfoTest() {
}
public UserInfoTest(int id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, precision = 0)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "roll_no", nullable = true)
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
And your query is "select id, name from users where roll_no = 1001".
Here query will return an object with id and a name column. Your Response class is like below:
Your Response class is like:
public class UserObject{
int id;
String name;
String rollNo;
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
here UserObject constructor will get an Object Array and set data with the object.
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
Your query executing function is like bellow :
public UserObject getUserByRoll(EntityManager entityManager,String rollNo) {
String queryStr = "select id,name from users where roll_no = ?1";
try {
Query query = entityManager.createNativeQuery(queryStr);
query.setParameter(1, rollNo);
return new UserObject((Object[]) query.getSingleResult());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
Here you have to import bellow packages:
import javax.persistence.Query;
import javax.persistence.EntityManager;
Now your main class, you have to call this function. First get EntityManager and call this getUserByRoll(EntityManager entityManager,String rollNo) function. Calling procedure is given below:
Here is the Imports
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
get EntityManager from this way:
#PersistenceContext
private EntityManager entityManager;
UserObject userObject = getUserByRoll(entityManager,"1001");
Now you have data in this userObject.
Note:
query.getSingleResult() return a object array. You have to maintain the column position and data type with query column position.
select id,name from users where roll_no = 1001
query return a array and it's [0] --> id and 1 -> name.
More info visit this thread .

There is no special support for this. But what you can do is create a custom method with a String parameter and in your implementation get the EntityManager injected and execute it.
Possibly helpful links:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
How to access entity manager with spring boot and spring data
Note: I would reconsider if what you are trying to do is a good idea because it bleeds implementation details of the repository into the rest of the application.

if you want to add custom query you should add #Param
#Query("from employee where name=:name")
employee findByName(#Param("name)String name);
}
this query will select unique record with match name.this will work

Thank you #ilya. Is there an alternative approach to achieve this task using Spring Data JPA? Without #Query annotation?
I just want to act on this part. yes there is a way you can go about it without using the #query annotation. what you need is to define a derived query from your interface that implements the JPA repository instance.
then from your repository instance you will be exposed to all the methods that allow CRUD operations on your database such as
interface UserRepository extends CrudRepository<User, Long> {
long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}
with these methods spring data will understand what you are trying to archieve and implement them accordingly.
Also put in mind that the basic CRUD operations are provided from the base class definition and you do not need to re define them. for instance this is the JPARepository class as defined by spring so extending it gives you all the methods.
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
Optional<T> findById(ID primaryKey);
Iterable<T> findAll();
long count();
void delete(T entity);
boolean existsById(ID primaryKey);
}
For more current information check out the documentation at https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

Based on #jelies answer, I am using the following approach
You can create another interface for your custom methods (as example MyQueryCustom) and then implement it as follows.
public class MyQueryRepositoryImpl implements MyQueryRepositoryCustom {
#PersistenceContext
private EntityManager entityManager;
public int executeQuery(String query) {
return entityManager.createNativeQuery(query).executeUpdate();
}
}
This will execute a custom query.

Related

findAll returns empty list

This is Repository interface
#Repository
public interface MenuStructureTblService extends JpaRepository<MenuStructureTbl, Integer> {
}
MenuStructureTbl Table
#Data
#Entity(name = "menu_structure_tbl")
public class MenuStructureTbl {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Integer menuId;
public String menuTitle;
public String menuText;
public Integer menuMotherId;
public String createDate;
public String updateDate;
}
I am banging my head on the wall, Returned list is empty.
This one returns null
#Repository
public interface MenuStructureTblService extends JpaRepository<MenuStructureTbl, Integer> {
#Query(value="SELECT * FROM menu_structure_tbl", nativeQuery = true)
Object justGetIT();
}
Returns null; I have checked the query, generates result. But, I do not know what is going on.
Let me know if you have any question.
My mistake, Conflict of Environment, i was checking against wrong environment.

Transactional method not write updates on database

I'm working on a Spring application that uses Hibernate 4.3 to manage data stored in a MySql database.
I want to simply update the value of this object:
#Table(name = "CONTENT")
public class KcContent {
#Id
#GeneratedValue
#Column(name = "ID")
#DocumentId
private Long id;
#Column(name="PRIORITY")
private Long priority;
// getter and setter
}
This is the point where I update the object:
private EntityManager entityManager;
#Transactional
public String checkAndManageActionsNewsletter(NewsletterActionRequestBean requestBean) {
List<String> newsIds = requestBean.getNewsIds();
// update priority based on position in list
for(long priority = 0; priority < newsIds.size(); priority++) {
String newsId = newsIds.get((int) priority);
long contentId = Long.parseLong(newsId);
KcContent news = getServiceCatalog().getContentService().find(contentId);
news.setPriority(priority);
}
}
Where #Transactional is imported from org.springframework.transaction.annotation.Transactional.
This code results in no update on database. I also tried to add after setPriority():
this.entityManager.merge(news)
this.entityManager.persist(news)
but it doesn't work.
I checked the reference to the object in the entityManager after the setPriority() method, and the value it's updated.
In the database instead the value is still the old value (0):
In the end, I tried to change values on database for the priority, and the application read them correctly, so the problem seems to be only on update.
There is some mistakes in the configuration?
Note: unfortunately I can't use JpaRepository.
Update:
This is the implementation of the find() method in the ContentService class:
public EntityManager getEntityManager() {
return entityManager;
}
#PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
#Transactional(readOnly=true)
public KcContent find(Long id) {
if (id == null){
return null;
}
return getEntityManager().find(KcContent.class, id);
}

How to get a static final property from a property file with spring boot

I've a spring boot project working on mysql DB. To encrypt some column I used the mysql function aes_encrypt, aes_decrypt and I stored my secret_key in a property file. Now to make some query on db I've to use in the crudrepository some native query like this.
#Query(value = "select * from a_table at where AES_DECRYPT(at.column1, +StaticClass.KEY+)= 'ABC'", nativeQuery = true)
public List<A>findByParameter();
I get this error "The value for annotation attribute Query.value must be a constant expression"
But the property KEY is already a FINAL property instantiate in this way
public class StaticClass {
static {
Properties prop = new Properties();
InputStream input;
try {
input = new FileInputStream("application.properties");
prop.load(input);
} catch (IOException e) {
logger.error("Error");
}
if(prop.getProperty("property1")==null) {
logger.error("property not found");
}
KEY=prop.getProperty("property1", "");
}
public static final String KEY;
One solution is to store the key in the code, but i need to keep the key in a property file. How can solve this problem?
Updated 1
I tried also in this way
public class StaticClass {
#Value(value="property1")
public static final String KEY;
}
You can create for this constants like below.
public enum Foo {
FOO("app.foo"), FOO_BAR("app.foo.bar");
private Environment environment;
private final String propertyKey;
Foo(String propertyKey) {
this.propertyKey = propertyKey;
}
public String getValue() {
return environment.getProperty(propertyKey);
}
private void setEnvironment(Environment environment) {
this.environment = environment;
}
#Component
private static class EnvironmentInjector {
#Autowired
private Environment environment;
#PostConstruct
private void postConstruct() {
for (Foo fT : EnumSet.allOf(Foo.class))
fT.setEnvironment(environment);
}
}
}
then you can use like this :
Foo.FOO.getValue()
#Query(value = "select * from a_table at where AES_DECRYPT(at.column1, +Foo.FOO.getValue()+)= 'ABC'", nativeQuery = true)
public List<A>findByParameter();

Spring MVC + Jackson: field not being serialized

I am trying to make a simple round-trip with a REST API that leads to storing an entity into the db and then returns the stored entity.
Going down works fine and the entity is stored and correctly returned to the REST Controller. However, when I return it, Jackson seems to serialize it incorrectly, as the "name" attribute is not included.
This is the entity:
#Entity
#Configurable
public class MyEntity extends IdentifiableEntity {
private String name;
protected MyEntity() {
};
public MyEntity(String name) {
this.name = name;
}
}
and the extended entity:
#Configurable
#Inheritance(strategy = InheritanceType.JOINED)
#Entity
public abstract class IdentifiableEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version = 1;
public String toString() {
return ReflectionToStringBuilder.toString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getVersion() {
return this.version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
The REST controller is:
#RestController
#RequestMapping("/service")
public class Service {
#RequestMapping(value = "/public/{name}", method = RequestMethod.GET)
public MyEntity storeEntityPublic(#PathVariable String name) {
System.out.println("Hello " + name
+ ", I am saving on the db. (PUBLIC)");
MyEntity saved = controller.saveEntity(name);
return saved;
}
}
Then my business logic:
#Service
public class LogicController {
#Autowired
private MyEntityRepository myEntityRepository;
public MyEntity saveEntity(String name) {
MyEntity cg = new MyEntity(name);
return myEntityRepository.save(cg);
}
}
I am using Spring repositories:
#Repository
public interface MyEntityRepository extends JpaSpecificationExecutor<MyEntity>,
JpaRepository<MyEntity, Long> {
}
The returned JSON is:
{"id":12,"version":1}
Where is my "name" attribute? Is is set in the variable being returned by the REST controller.
I found the trick: MyEntity needs to have a public get for the property that has to be shown. A good reason to use a DTO pattern.
In response to your "I don't want to have my Entity "dirty"" comment: Jackson allows the use of so-called Mixins. They allow you to define annotations for your class outside the class itself. In your case it could look like this:
public abstract class MyEntityMixin {
#JsonProperty
private String name;
}
You may keep it as a field and annotate the field with #JsonProperty if you like.

Insert the table elements in Collection

HI,
I have a collection and i wanna insert all the elements of my table.
How i can do this operation in EJB QL?
For example: (this isn't my code)
I have my Collection:
Collection<Person> coll = new Collectio<Person>
And i have my table Person
#entity
private class Person{
private id;
private name;
//getters setter
}
I have popolate the table and i wanna all the element in my collection.
Thanks.
Introduce Query in your JPA entity:
#Entity
#NamedQuery(name = "Person.findAll", query = "SELECT p from Person p")
public class Person {
private id;
private name;
//getters setter
}
Then inject EntityManager in your code and use query:
public class MyClass {
#PersistenceUnit(name = "MyEntitiesFromPersistenceXML")
private EntityManagerFactory emf;
public void myMethod() {
EntityManager entityManager = emf.createEntityManager();
Query query = entityManager.createNamedQuery("Person.findAll");
#SuppressWarnings("unchecked")
List<Person> persons = query.getResultList();
// query returns List which, in turn, extends Collection
}
}
Btw, why your class is private? It must be public.