I have some troubles in my app using sqlRestriction method:
This is my movie class:
public class Movie {
#Id
#Column(name = "pk")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "movie_seq")
#SequenceGenerator(name = "movie_seq", sequenceName = "movie_seq", allocationSize = 1, initialValue = 1)
public long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "name")
public String str_name = "";
...
}
When i do something like that (dummy test):
List<Movie> movies = session.createCriteria(Movie.class)
.add(Restrictions.sqlRestriction("1 in (1)"))
.list();
It Works perfectly.
My second test (changing the Restriction):
.add(Restrictions.sqlRestriction("Movie.id in (166,171)"))
And my third test
add(Restrictions.sqlRestriction("{Movie}.id in (166,171)"))
Both of them fails in the same way:
ERROR: Token SQL92 no soportado en la posición: 345
Exception in thread "main" org.hibernate.exception.GenericJDBCException: could not extract ResultSet
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
...
Caused by: java.sql.SQLException: Token SQL92 no soportado en la posición: 345
at oracle.jdbc.driver.OracleSql.handleODBC(OracleSql.java:1275)
...
What am I doing wrong?
Edited
I tried :
.add(Restrictions.sqlRestriction("{alias}.id in (?, ?)", new Long[]{166L,171L}, new Type[]{Hibernate.Long, Hibernate.Long}) )
And i got this : "Long cannot be resolved or is not a field"
It seems "Hibernate.{AnyType}" is deprecated, see this link: Why Hibernate STRING can not be resolved?
i tried this instead:
.add(Restrictions.sqlRestriction("{alias}.id in (?, ?)", new Long[]{166L,171L}, new Type[]{LongType.INSTANCE, LongType.INSTANCE}) )
But I got this error:
Caused by: java.sql.SQLSyntaxErrorException: ORA-00904: "THIS_"."ID": invalid identifier
Not sure if you use the alias correctly. Try following
Restrictions.sqlRestriction("{alias}.pk in (?, ?)", new Long[]{166L,171L}, new Type[]{Hibernate.Long, Hibernate.Long}) )
Count of ? needs to the same as the size of given array so the (?,?) should be constructed & appended dynamically based on the array passed. As well as the type array.
EDIT: the id field name is not id but pk, missed #Column(name = "pk") in origal answer. Newer hibernate versions have no more 'Hibernate.Long' but use LongType.INSTANCE instead.
Restrictions.sqlRestriction("{alias}.pk in (?, ?)", new Long[]{166L,171L}, new Type[]{LongType.INSTANCE, LongType.INSTANCE}) )
Solved.
I was using the name of the hibernate table/field names (I mean "id") it was changed to "pk". and it worked!.
.add(Restrictions.sqlRestriction("pk in (?, ?)", new Long[]{166L,171L}, new Type[]{LongType.INSTANCE, LongType.INSTANCE}) )
I thought I have to use the HQL table/field names. But the names/fields of the phisicall table is the correct!.
Related
Something very bizarre have been happening. I have a very simple Entity recipe like so
#Data
#Entity
#Table(name = "recipe", schema = "public")
#AllArgsConstructor
#NoArgsConstructor
public class Recipe {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "instructions")
private String instructions;
#Column(name = "date_added", nullable = false)
private String dateAdded;
#Column(name = "last_edited", nullable = false)
private String lastEdited;
}
and I have this post service that should post the 4 string attribute to the database
public void postRecipe(Recipe recipe){
var sql = """
INSERT INTO public.recipe ("name","instructions","date_added","last_edited")
VALUES (?, ?, ?, ?)
""";
jdbcTemplate.update(
sql,
recipe.getName(),
recipe.getInstructions(),
recipe.getDateAdded(),
recipe.getLastEdited()
);
}
However when the following jason is sent using postman, I get the null value error.
{
"name":"test",
"instructions":"don't eat",
"date_added":"03/04/2017",
"last_edited":"03/04/2017"
}
ERROR: null value in column \"date_added\" of relation \"recipe\" violates not-null constraint\n Detail: Failing row contains (3, null, don't eat, null, test)
The strangest thing is that only the "name" and "instruction" columns receive their data and not the other columns. I have tried adding another String attribute to the Entity class and it also cannot get data from the jason.
Edit 1:
I have tried adding the data directly through pgadmin and it worked fine
INSERT INTO recipe (name, instructions, date_added, last_edited)
VALUES ('test', 'test instruction', '2020/03/05', '2020/05/08');
It looks like your deserialization is broken - transforming your JSON into the Java entity, which results in some null values present. Most likely because date_added != dateAdded (field name), and Jackson cannot properly set a value.
I recommend having a look at Jackson guide: https://www.baeldung.com/jackson-annotations, #JsonProperty specifically. And overall do not mix entities and DTOs
After many trials and errors I was able to come up with a solution but still have no clue as to why this is happening. It turns out the under score in the annotation is the problem.
//won't work
#Column(name = date_added)
//works
#Column(name = dateadded)
This is pretty strange because I am fairly certain that the under score is generated by hibernate.
if anyone know why this is happening please let me know... for now I will just stay away from the under scrolls.
I am getting the error in an Hibernate project.There is a Kisi class. Here is the class structure of my project:
Kisi entity:
#Entity
#Table(name = "KISI" )
public class Kisi implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 3283201458992402052L;
#Id
#Column(name = "ANAHTAR")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "kisi_Sequence")
#SequenceGenerator(name = "kisi_Sequence", sequenceName = "KISI_SEQ" , allocationSize = 1)
private int anahtar;
#ManyToOne(fetch = FetchType.LAZY, cascade= {CascadeType.ALL})
#JoinColumn(name = "UNVANANAHTAR", nullable = true)
private Unvan unvani;
#Column(name = "ADI", columnDefinition = "VARCHAR2(700 Byte)", nullable = false)
private String adi;
#Column(name = "SOYADI", columnDefinition = "VARCHAR2(1000 Byte)", nullable = false)
private String soyadi;
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private Kimlik kimligi;
#Column(nullable = true)
private LocalDate dogumTarihi;
#Enumerated
#Column(name="CINSIYET", columnDefinition = "smallint", nullable = true)
private Cinsiyet cinsiyeti;
#Enumerated
#Column(name="MEDENIDURUMU", columnDefinition = "smallint", nullable = true)
private MedeniDurum medeniDurumu;
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private KisiDetay kisiDetay;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "kisisi")
private Set<KisiIletisim> iletisimKumesi;
public Kisi() {
super();
String bosSozce = Sabit.BOSSOZCE;
this.anahtar = Integer.MIN_VALUE;
this.unvani = null;
this.adi = bosSozce;
this.soyadi = bosSozce;
this.kimligi = null;
this.dogumTarihi = Sabit.BOSTARIH;
this.cinsiyeti = Cinsiyet.VARSAYILAN;
this.medeniDurumu = MedeniDurum.VARSAYILAN;
this.iletisimKumesi = new HashSet<KisiIletisim>(0);
this.kisiDetay = null;
}
// Constructors, getters and setters
EDIT-1
Andreas pointed out that the definition of enum is absent and not appropriate in the class. Kisi utilizes two enums, Cinsiyet (Gender) and MedeniDurum (Marital Status). Even though I changed it, problem still persists.
public enum Cinsiyet {
VARSAYILAN,
KADIN,
ERKEK
}
public enum MedeniDurum {
VARSAYILAN,
EVLI,
BEKAR,
DUL,
}
Database table structure is as below:
Database Table of Kisi entity:
When I try to insert Kisi , I am getting the error:
Error : 932, Position : 121, Sql =insert into KISIPROFIL.KISI (ADI, CINSIYET, dogumTarihi, MEDENIDURUMU, SOYADI, UNVANANAHTAR, ANAHTAR) values (:1 , :2 , :3 , :4 , :5 , :6 , :7 ),
Error Msg = ORA-00932: inconsistent data types: should be: NUMBER received: BINARY
original sql:
insert into KISIPROFIL.KISI (ADI, CINSIYET, dogumTarihi, MEDENIDURUMU,
SOYADI, UNVANANAHTAR, ANAHTAR) values (?, ?, ?, ?, ?, ?, ?)
sql:
insert into KISIPROFIL.KISI (ADI, CINSIYET, dogumTarihi, MEDENIDURUMU,
SOYADI, UNVANANAHTAR, ANAHTAR) values (:1 , :2 , :3 , :4 , :5 , :6 ,
:7 )
OracleErrorNumber:
932
stacktrace
org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2886)org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3386)org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:8
How can I overcome this problem?
Thanks in advance.
Oracle Version:
Personal Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
References:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.0.7.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.6.Final</version>
</dependency>
The problem has been initiated from the "private LocalDate dogumTarihi;". The LocalDate cannot convert to DATE directly. As Thorben Janssen pointed out that "JPA will map it to a BLOB instead of a DATE or TIMESTAMP. That means the database is not aware of the date object and cannot apply any optimization for it. " https://thorben-janssen.com/persist-localdate-localdatetime-jpa/
It has nothing to do with enum values. You should either
1- convert LocalDate to Date in class decleration. (dogumTarihi shall be "private Date dogumTarihi;")
2- You should write a converter.
I learn Spring Boot reading book Spring in Action 5. I try to insert data to H2 embedded database.I use SimpleJdbcInsert and executeAndReturnKey method.
Here is a Constructor:
#Autowired
public JdbcOrderRepository(JdbcTemplate jdbc) {
this.orderInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order").usingGeneratedKeyColumns("id");
this.orderTacoInserter = new SimpleJdbcInsert(jdbc).withTableName("Taco_Order_Tacos");
this.objectMapper = new ObjectMapper();
}
Here is methods to insert data:
#Override
public Order save(Order order) {
order.setPlacedAt(new Date());
long orderId = saveOrderDetails(order);
order.setId(orderId);
List<Taco> tacos = order.getTacos();
for (Taco taco : tacos)
saveTacoToOrder(taco, orderId);
return order;
}
private long saveOrderDetails(Order order) {
Map<String, Object> values = objectMapper.convertValue(order, Map.class);
values.put("placedAt", order.getPlacedAt());
long orderId = orderInserter.executeAndReturnKey(values).longValue();
return orderId;
}
The error is on this string:
long orderId = orderInserter.executeAndReturnKey(values).longValue();
Error text:
There was an unexpected error (type=Internal Server Error, status=500).
PreparedStatementCallback; ???????? NULL ?? ????????? ??? ????
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement:
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY,
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]; nested exception is
org.h2.jdbc.JdbcSQLException: ???????? NULL ?? ????????? ??? ????
"DELIVERYNAME" NULL not allowed for column "DELIVERYNAME"; SQL statement:
INSERT INTO Taco_Order (DELIVERYNAME, DELIVERYSTREET, DELIVERYCITY,
DELIVERYSTATE, DELIVERYZIP, CCNUMBER, CCEXPIRATION, CCCVV, PLACEDAT)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) [23502-197]
Bur in debugger evrything is alright:
value parameter is fully loaded by values.
idea screensot
Class Order:
#Data
public class Order {
#NotBlank(message = "Name is required")
private String name;
#NotBlank(message = "Name is required")
private String street;
#NotBlank(message = "Name is required")
private String city;
#NotBlank(message = "Name is required")
private String state;
#NotBlank(message = "Name is required")
private String zip;
#CreditCardNumber(message = "Not valid cc")
private String ccNumber;
#Pattern(regexp = "^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message = "Must be formatted MM/YY")
private String ccExpiration;
#Digits(integer = 3, fraction = 0, message = "Invalid CVV")
private String ccCVV;
private Long id;
private Date placedAt;
List<Taco> tacos = new ArrayList<>();
public void addDesign(Taco design) {
this.tacos.add(design);
}
}
How to fix this problem and insert data to H2 database using SimpleJdbcInsert?
I faced same issue today. I was able to resolve this.
To fix this remove, go to schema.sql(file in which you are creating tables) and remove delivery from fields in Taco_Order table.
From book
SimpleJdbcInsert has a couple of useful methods for executing the insert: execute() and executeAndReturnKey(). Both accept a Map, where the map keys correspond to the column names in the table the data is inserted into. The map values are inserted into those columns.
Keys name are fields from Order.java
I went through the screen shot and i do not see any value for column DELIVERYNAME.
The issue is because of the table in which you are running the insert operation doesn't allow null values for the column DELIVERYNAME.
Ways to solve this:-
Remove the NOT NULL constraints from the column in the table definition.
Remove the #NotBlank annotation from the members of the object if you are not sure whether the data is going to be blank or not.
Use proper mapping between the column names in table and those with the member name in Class/Entity Definition. You can use #Column annotation to easily do that.
As much I can understand following your code and screenshot, that the issue is because of improper mapping between column names and that of the member of the object.
It is recommended that rather than relying on the auto mapping of Spring Boot coder should manually do the mapping using #Column annotation or mapping through hibernate configuration file.
The issue is with attributes name in Order class. These should mirror the coloumn name of respective table.
Eg:
private String deliveryName; -> would be mapped to deliveryName field in db.
The issue has arisen as in schema.sql the field is named as "deliveryName" while in ingredient class the attirbute is named "name".
This answer is for those who follow the examples from book "Spring in action". You won't do such thing in real life, but if you're a reader of this book it is reasonable to replace this:
Map<String, Object> values = objectMapper.convertValue(order, Map.class);
values.put("placedAt", order.getPlacedAt());
with this:
Map<String,Object> values = new HashMap<>();
values.put("ID", order.getId());
values.put("PLACEDAT", order.getPlacedAt());
values.put("DELIVERYNAME", order.getName());
values.put("DELIVERYSTREET", order.getStreet());
values.put("DELIVERYCITY", order.getCity());
values.put("DELIVERYSTATE", order.getState());
values.put("DELIVERYZIP", order.getZip());
values.put("CCNUMBER", order.getCcNumber());
values.put("CCEXPIRATION", order.getCcExpiration());
values.put("CCCVV", order.getCcCVV());
Previous answers mentioned changing schema which will cause problems if you will follow examples from next chapters or using jpa annotations to perform mapping, but according to the author of the book you don't know about them because they are introduced in the next chapters. Besides you won't use JdbcOrderRepository class from the next chapter.
Names of fields in Order class must be the same as columns names in table Taco_Order.
#Data
public class Order {
private Long id;
private Date placedAt;
#NotBlank(message = "Podanie imienia i nazwiska jest obowiazkowe")
private String deliveryName;
#NotBlank(message = "Podanie ulicy jest obowiązkowe")
private String deliveryStreet;
#NotBlank(message = "Podanie miasta jest obowiązkowe")
private String deliveryCity;
#NotBlank(message = "Podanie województwa jest obowiązkowe")
private String deliveryState;
#NotBlank(message = "Podanie kodu pocztowego jest obowiązkowe")
private String deliveryZip;
#CreditCardNumber(message = "To nie jest prawidłowy numer karty kredytowej")
private String ccNumber;
#Pattern(regexp = "^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message = "Wartość musi być w formacie MM/RR")
private String ccExpiration;
#Digits(integer = 3, fraction = 0, message = "Nieprawidłowy kod CVV")
private String ccCVV;
public List<Taco> tacos = new ArrayList<>();
public void addDesign(Taco design) {
this.tacos.add(design);
}
public List<Taco> getTacos() {
return tacos;
}
}
I encountered the same problem today and all I had forgotten to do was to update the fields in the order.html file to deliveryName, deliveryCity etc.
I have a Counter entity as below : for each prefix (current year and month), I am maintaining a counter that I need to increment.
#Entity
#Table(name = "T_COUNTER")
#SequenceGenerator(allocationSize = 1, name = "S_COUNTER", sequenceName = "S_COUNTER")
public class CodeCounter implements Serializable {
private static final long serialVersionUID = 6431190800245592165L;
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "S_COUNTER")
#Column(name = "ID")
private Long id;
#Column(name = "PREFIX", nullable = false,unique = true)
private String prefix;
#Column(name = "COUNTER", nullable = false)
private Integer counter;
This is my very simple JPA repository, using Spring data :
public interface CodeCounterRepository extends JpaRepository<CodeCounter, Long> {
#Transactional
CodeCounter findByPrefix(String prefix);
}
Now, whenever my service is called, I need to get the right counter thanks to the prefix (and create it if it doesn't exist yet, the first time of the month), increment and save back. This is how I have implemented it so far :
//entry point in the service
public String generateUniqueRequestCode() {
System.out.println("Generating new Request Code for Consolidated Request");
Integer counter = getUniqueCodeAndUpdateCounter(calendarProvider);
String requestCode = format("%s_%04d_%02d_%06d", REQUEST_CODE_PREFIX, calendarProvider.getCurrentYear(), calendarProvider.getCurrentMonth(),
counter);
System.out.println("New Request Code for Consolidated Request:: " + requestCode);
return requestCode;
}
#Transactional
private synchronized Integer getUniqueCodeAndUpdateCounter(CalendarProvider calendarProvider) {
System.out.println("entering..");
String prefix = format("%04d_%02d", calendarProvider.getCurrentYear(), calendarProvider.getCurrentMonth());
CodeCounter codeCounter = codeCounterRepository.findByPrefix(prefix);
if (codeCounter != null) {
codeCounter.setCounter(codeCounter.getCounter() + 1);
} else {
codeCounter = new CodeCounter();
codeCounter.setPrefix(prefix);
codeCounter.setCounter(1);
}
CodeCounter counter=codeCounterRepository.save(codeCounter);
int result=counter.getCounter();
System.out.println("..exiting");
return result;
}
I've added a multithreaded unit test (using tempus fugit library ) with H2 DB, that shows it's working when 2 threads try to generate a unique code at the same time, but I'm not too happy with my code : I would like to get rid of that synchronized method and rely solely on proper transaction configuration.
If I remove the synchronized keyword, then both threads execute the method at same time and it fails because they generate the same prefix, which shouldn't happen (Unique index or primary key violation). Here's the log :
Generating new Request Code for Consolidated Request
Generating new Request Code for Consolidated Request
entering..
entering..
Hibernate: select codecounte0_.id as id1_4_, codecounte0_.counter as counter2_4_, codecounte0_.prefix as prefix3_4_ from t_counter codecounte0_ where codecounte0_.prefix=?
Hibernate: select codecounte0_.id as id1_4_, codecounte0_.counter as counter2_4_, codecounte0_.prefix as prefix3_4_ from t_counter codecounte0_ where codecounte0_.prefix=?
Hibernate: call next value for S_COUNTER Hibernate: call next value for S_COUNTER
Hibernate: insert into t_counter (counter, prefix, id) values (?, ?, ?)
Hibernate: insert into t_counter (counter, prefix, id) values (?, ?, ?)
..exiting
New Request Code for Consolidated Request:: CR_2016_09_000001
17:02:39.853 WARN SqlExceptionHelper - SQL Error: 23505, SQLState: 23505
17:02:39.854 ERROR SqlExceptionHelper - Unique index or primary key violation...
Any idea of how to implement this without synchronizing myself in the code ?
Is writing a method in your entity that is annotated with #PrePersist or #PreUpdate something that you are looking for?
I've mapped my class as follow (omitted other fields as only ID matters):
#Entity
#Table(name = "MODEL_GROUP")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class SettlementModelGroup implements Serializable
{
#Id
#GeneratedValue(generator = "MODEL_GROUP_SEQ", strategy = GenerationType.SEQUENCE)
#GenericGenerator(name = "MODEL_GROUP_SEQ",
strategy = "sequence",
parameters = #Parameter(name = "sequence", value = "SEQ_MODEL_GROUP_MODEL_GROUP_ID"))
#Column(name = "MODEL_GROUP_ID", nullable = false)
private Integer modelId;
}
when I'm saving new object:
Integer modelGroupId = sessionFactory.getCurrentSession().save( modelGroup );
System.out.println( modelGroupId );
ID is set as for example 23, but when I look at the database it is actually 24. This is leading to many problems, as I'm using this ID later on. Any idea why it is making this gap?
SQL logs show that everything is fine (I thinks so):
Hibernate:
select
SEQ_MODEL_GROUP_MODEL_GROUP_ID.nextval
from
dual
Hibernate:
insert
into
MODEL_GROUP
(DOMAIN_ID, DESCRIPTION, NAME, PERIOD_TYPE_ID, MODEL_GROUP_TYPE_ID, STATUS_ID, OWNER_ID, MODEL_GROUP_ID)
values
(?, ?, ?, ?, ?, ?, ?, ?)
Trigger and Sequence:
CREATE SEQUENCE "SEQ_MODEL_GROUP_MODEL_GROUP_ID"
INCREMENT BY 1
START WITH 1
NOMAXVALUE
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER
;
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;
Apparently, when Hibernate ask your database for nextValue of ID, it fires also Trigger. So when I ask for ID, I've got number 23 but when actually saving to database by commiting transaction, it is increased again so I've got 24. Solution is described here:
HIbernate issue with Oracle Trigger for generating id from a sequence
To make it work correctly, I changed Trigger:
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;