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
Related
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);
}
I am struggling with Spring Boot MongoDB cascade operations on referenced objects. Below are MongoDB document schema classes.
== Post
#Getter
#Setter
#Document(collection="Post") // (1)
public class Post {
#Id
private String _id;
#Indexed(unique = true)
private Long id;
private String title;
private String body;
private Date createdDate;
#DBRef(db = "User", lazy = true)
private User user;
#DBRef(db = "Tag", lazy = true)
private Collection<Tag> tags;
== User
#Getter
#Setter
#Document(collection="User") // (1)
public class User {
#Id //(2)
private String _id;
#Indexed(unique = true)
private Long id;
#Indexed(unique=true) // (3)
private String username;
private String password;
private String email;
private String fullname;
private String role;
}
== Tag
#Getter
#Setter
#Document(collection="Tag")
public class Tag {
#Id
private String _id;
#Indexed(unique = true)
private Long mid;
private String body;
private Date createdDate;
#DBRef(db = "User", lazy = true)
private User user;
}
But #DBRef annotation does not work at all. It throws the following exception.
2019-03-01 14:54:10.411 ERROR 5756 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.mapping.MappingException: Cannot create a reference to an object with a NULL id.] with root cause
org.springframework.data.mapping.MappingException: Cannot create a reference to an object with a NULL id.
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.createDBRef(MappingMongoConverter.java:975) ~[spring-data-mongodb-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:597) ~[spring-data-mongodb-2.1.4.RELEASE.jar:2.1.4.RELEASE]
When the json file is imported into MongoDB schema, the above error is shown. I found some reference site with googling which said to generate new event source using CascadingMongoEventListener class and user-defined #CascadeSave annotation. But I think there are another solutions with some cascade annotations. Any idea,please.
Mongo doesn't support the relationship between documents. Due to this cascade operation doesn't support in spring data mongo. you can do it in two manners.
1) Make your own cascade handler(best way to do is to use spring event publisher) But it can also be done using custom handler without spring event see here.
2) Make an explicit call to referenced DB for operation.
Take a look at RelMongo which is a framework built on top of Spring Data and which make possible cascading, fetching.. and even lookups which are not possible with DBRefs
This question already has answers here:
Status 400 and Error deserializing List of Objects. No default constructor found
(2 answers)
Closed 4 years ago.
Using Spring I created a microservice that finds all messages in DB for a specific user.
Controller:
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public List<MessageObject> enviados (#RequestParam("mail") String mail) {
return daoMsg.findByEmisorOrDestinatario(mail, mail);
}
}
DAO:
public class MessageObject implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String emisor;
private String destinatario;
private String mensaje;
private String tipo;
private LocalDate fecha;
private String id_housing;
public MessageObject() {
}
public MessageObject(String id, String emisor, String destinatario, String tipo, LocalDate fecha, String id_housing) {
this.id = id;
this.emisor = emisor;
this.destinatario = destinatario;
this.tipo = tipo;
this.fecha = fecha;
this.id_housing = id_housing;
}
When calling the microservice from my client app:
Client client = ClientBuilder.newClient();
WebTarget webResource =
client.target("http://localhost:8082").path("Mensajes").queryParam(mail);
Invocation.Builder invocationBuilder =
webResource.request(MediaType.APPLICATION_JSON);
Response respuesta = invocationBuilder.get();
int status = respuesta.getStatus();
System.out.println("el status es "+ status);
MessageObject[] listMessages =
respuesta.readEntity(MessageObject[].class);
stacktrace:
javax.ws.rs.ProcessingException: Error deserializing object from entity
stream.Caused by: javax.json.bind.JsonbException: Can't create instance of
a class: class [LMessages.MessageObject;, No default constructor
found.Caused by: java.lang.NoSuchMethodException:
[LMessages.MessageObject;.<init>()
My client has the same MessageObject DAO as the one in the microservice, in:
Question: why JSON is looking for a default constructor in LMessages.MessageObject if my MessageObject class is in package Messages (not LMessages)
Solved. Problem was i was using .queryparam(mail) without key - value structure, only key. Now using .queryparam ("mail", mail) works.
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've been getting this strange problem with MySQL VARCHAR data type.
I have this database table in MySQL, there are two fields of VARCHAR data type,let's call these field First_name and last_name. Problem is, when querying for these particular fields using Spring JdbcTemplate or using Spring Data JpaRepository interface, it always return an empty string or white spaces even though these fields have values.
I also tried to update the values into numeric like 12345 for both fields and alter the data type into INT and had the JdbcTemplate to queryForInt and it's giving me the correct result.
Here's the Entity class:
#Entity
#Table(name = "tb_user")
public class AppUser extends BaseEntity{
private static final long serialVersionUID = -1008862792657260298L;
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#Column(name = "id")
private Integer id;
#Column(name = "first_name", length = 30, nullable = false)
String firstName;
#Column(name = "last_name", length = 30)
String lastName;
}
The code used for testing:
//code #1
JdbcTemplate jt = new JdbcTemplate(ds);
String sql = "select first_name from tb_user where id=3";
JdbcTemplate jt = new JdbcTemplate(ds);
try {
System.out.println(jt.queryForObject(sql, String.class)); //returns empty string/white space instead of actual value, e.g: JOHN
} catch (Exception e) {
e.printStackTrace();
} finally{
}
//code #2
String sql = "select first_name, last_name from tb_user where id=3";
JdbcTemplate jt = new JdbcTemplate(ds);
try {
jt.query(sql, new RowMapper<UserDTO>(){
#Override
public UserDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
System.out.println(rs.getString("first_name") + " " + rs.getString("last_name")); //also returns empty string/white spaces
return null;
}
});
} catch (Exception e) {
e.printStackTrace();
} finally{
}
Please advice. Thank you.
-Ipie-