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);
}
Related
I have this JPA Class, where I have 3 columns id, name and date. The Database is already filled with data, where each entry has an id.
#Data
#Entity
#Table(name = "TEST", schema = "TESTSCHEMA")
public class TestDataJpaRecord implements Serializable {
private static final long serialVersionUID = 1L;
TestDataJpaRecord(){
// default constructor
}
public TestDataJpaRecord(
String name,
Date date,
){
this.name = name;
this.date = date;
}
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "TEST_SEQUENCE")
#SequenceGenerator(
sequenceName = "TEST_SEQUENCE", allocationSize = 1,
name = "TEST_SEQUENCEx")
private Long id;
#Column(name = "NAME")
private String name;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DATE")
private Date date;
}
I created a JPA repository for all the data.
public interface TestDataJpaRecordRepository extends JpaRepository<TestDataJpaRecord, Long> {
}
I want to get the data from the DB in a JSON format.
Here is my Rest GET Api. Here I return the data as a string just, but I want to return them as JSON.
#GetMapping(value = "data/{id}")
private ResponseEntity<?> getDataFromTheDB(#PathVariable("id") Long id) {
// get one entry form the DB
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
// Here I want to return a JSON instead of a String
return new ResponseEntity<>(testDataJpaRecord.toString(), HttpStatus.OK);
}
Any idea on how I could return the data as JSON and not as a string from the DB?
I would very very much appreciate any suggestion.
If you have Jackson on the classpath which you should if you have used the spring-boot-starter-web then simply:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord> getDataFromTheDB(#PathVariable("id") Long id) {
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
return new ResponseEntity.ok(testDataJpaRecord);
}
This assumes you have annoted your controller with #RestController rather than #Controller. If not then you can either do that or, annotate your controller method with #ResponseBody.
With Spring Data's web support enabled (which it should be by default with Spring Boot) then you can also simplify as below:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord>
getDataFromTheDB(#PathVariable("id") TestDataJpaRecord record) {
return new ResponseEntity.ok(record);
}
See:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.basic.domain-class-converter
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();
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.
I am currently facing a problem that makes me crazy.
I would like to create my own IdentifierGenerator to manage ids with a specific format.
public class CustomGenerator implements IdentifierGenerator {
private static final String REQUEST = "SELECT CURRVAL('sequence')";
private final String prefixFormat = "PREFIX-%s";
#Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
final String prefix = getPrefix();
Session sessionHibernate = (Session) session;
Query query = sessionHibernate.createSQLQuery(REQUEST );
Object obj = query.uniqueResult();
return null;
}
private String getPrefix() {
final Calendar date = Calendar.getInstance();
DateFormat dateFormat = new SimpleDateFormat("yyyyMM");
return String.format(prefixFormat, dateFormat.format(date.getTime()));
}
}
My Junit Test:
#Test
#Transactional
public void addEntity() {
final Entity entity= new Entity();
final long count = this.entityRepository.count();
this.entityRepository.saveAndFlush(rex);
assertEquals(count + 1, this.entityRepository.count());
}
My Entity:
#Entity
#Table(name = "ENTITY")
public class Entity {
#Id
#GenericGenerator(name = "sequence", strategy = "[...]CustomGenerator")
#GeneratedValue(generator = "sequence")
#Column(name = "ENTITY_ID")
private Integer entityId;
[...]
}
My JUnit test crashes - Stacktrace:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [SELECT CURRVAL('sequence_rex_id')]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:238)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:221)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
Database: HSQLDB (test), POSTGRESQL (production)
Does anyone have any idea ?
Sorry for any mistakes I could have made. English is not my mother tongue.
The SELECT CURRVAL('sequence') syntax is specific to PostgreSQL. When using PostgreSQL syntax in HSQLDB, you should include the necessary SQL syntax flag in the JDBC connection URL. For example jdbc:hsqldb:mem:test;sql.syntax_pgs=true
I'm facing a strange problem of "losing" data when updating data using Hibernate.
The application is deployed on JBoss AS 7, database connection isolation level to MySql DB is set to READ_COMMITED.
I have entities defined via inheritance like this:
#Entity
#Table(name = "element")
#Inheritance(strategy=InheritanceType.JOINED)
public class ElementEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="i_element", nullable=false)
private Integer iElement;
#Column(name="hash")
private String hash;
...
}
#Entity
#Table(name = "element_a")
#PrimaryKeyJoinColumn(name="i_element")
public class AElementEntity extends ElementEntity {
... // some primitive fields
}
#Entity
#Table(name = "element_b")
#PrimaryKeyJoinColumn(name="i_element")
public class BElementEntity extends ElementEntity {
... // some primitive fields
}
#Entity
#Table(name = "element_c")
#PrimaryKeyJoinColumn(name="i_element")
public class CElementEntity extends ElementEntity {
... // some primitive fields
#OneToMany(mappedBy="cElement", cascade=CascadeType.ALL)
private List<NodeEntity> nodes;
}
#Entity
#Table(name="node")
public class NodeEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "i_node")
private Integer iNode;
#ManyToOne
#JoinColumn(name="i_element", referencedColumnName="i_element")
private CElementEntity cElement;
... // some primitive fields
}
and dao implementation like this:
public class ElementDaoImpl implements ElementDao {
private static final Logger logger = Logger.getLogger(ElementDaoImpl.class);
private EntityManager entityManager;
public ElementDaoImpl(EntityManager entityManager) {
super();
}
#Override
public List<ElementEntity> load(String hash) {
// nacist vsechny elementy pro SGL
Query query = entityManager.createQuery("FROM " + ElementEntity.class.getSimpleName() + " WHERE hash = :hash");
query.setParameter("hash", hash);
List<ElementEntity> elements = (List<ElementEntity>) query.getResultList();
//.. log C element
return elements;
}
#Override
public void save(String hash, List<ElementEntity> elements) {
removeElementsByHash(hash);
for (ElementEntity element : elements) {
if (logger.isDebugEnabled()) {
if (element instanceof CElement) {
CElementEntity cElementEntity = (CElementEntity) element;
logger.debug("Storing for hash " + hash + " nodes " + cElementEntity.getNodes());
}
}
entityManager.persist(entity);
entityManager.flush();
}
entityManager.clear();
}
private void removeElementsByHash(String hash) {
Query query = getEntityManager().createQuery("FROM " + ElementEntity.class.getSimpleName() + " WHERE hash = :hash");
query.setParameter("hash", hash);
List<ElementEntity> entities = (List<ElementEntity>) query.getResultList();
if (entities != null && !entities.isEmpty()) {
for (ElementEntity entity : entities) {
entityManager.remove(entity);
}
}
entityManager.flush();
entityManager.clear();
}
}
dao is created in EJB stateless bean:
#Stateless(name=ElementServiceBean.NAME, mappedName=ElementServiceBean.NAME)
#Local({ElementService.class})
#LocalBinding(jndiBinding=ElementServiceBean.NAME)
public class ElementServiceBean implements ElementService {
public static final String NAME = "ElementServiceBean";
#PersistenceContext(unitName = "coredb_em")
protected EntityManager entityManager;
private ElementDao elementDao;
#PostConstruct
public void init() {
this.elementDao = new ElementDaoImpl(entityManager);
}
#Override
public List<ElementEntity> load(String hash) {
return elementDao.load(hash);
}
#Override
public void save(String hash, List<ElementEntity> elements) {
elementDao.save(hash, elements);
}
}
Now, in my app sometimes happens situation when save method is called by two different threads concurrently almost at the same time, which causes that data from the collection in CElementEntity are somehow lost.
I don't use any form of Hibernate locking. No exception is thrown. Just the data from the "nodes" collection is lost.
So my question is now, how is it possible? Do I use flush and clear methods in dao layer incorrectly?
Or do I really need any kind of hibernate/database locking (but why should I need it when I delete all element rows with given hash before I persist them)?
If I understand it well in CMT is commit performed when the transaction ends.
Here is the log4j log:
[2015-01-22 17:02:49,944] 9023579 [Thread-208] DEBUG ElementDaoImpl Storing for hash ABCDE nodes nodes=[NodeEntity[1], NodeEntity[2]]
[2015-01-22 17:02:50,028] 9023663 [Thread-216] DEBUG ElementDaoImpl Storing for hash ABCDE nodes nodes=[NodeEntity[1], NodeEntity[2]]
[2015-01-22 17:02:50,727] 9024362 [Thread-208] DEBUG ElementDaoImpl CElementEntity nodes: []]
Thank you for every tip or advice how to improve the code.
You need to add #Version to your entities to enable optimistic locking and therefore prevent lost updates.
For that your entities need to contain the following field:
#Version
private int version;
Hibernate will take care of the rest.