I want to try to make unit test with DBUnit but I have a problem with my dataset.
Here is my persistence object:
#Entity
#Table(name = "personnes")
public class Personne implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer pk;
#Column
private String name;
}
And my dataset:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<personnes name="toto" pk="1" />
</dataset>
My problem is with the name column, I get this error:
org.dbunit.dataset.NoSuchColumnException: personnes.NAME - (Non-uppercase input column: name) in ColumnNameToIndexes cache map. Note that the map's column names are NOT case sensitive.
I don't understand why dbunit search a column "NAME" whereas my column is "name".
Thanks for your help.
I've been fighting this for a while, and keep coming back to this issue, which doesn't seem to have a solution yet.
In Unitils 3.4.1, they added a new property, org.dbunit.database.IMetadataHandler.implClassName
In my unitils.properties file, I added the following line
org.dbunit.database.IMetadataHandler.implClassName=org.dbunit.ext.mysql.MySqlMetadataHandler
Yes, I know, according to Unitils' website, there is no version 3.4.1, but you can get the latest version via Maven.
link to issue report
Since you don't specify the column name in the mapping, I guess the underlying ORM framework generates the column name "NAME" for it.
To resolve this error/warning, you could add the column name to the mapping
#Column( name = "name")
resulting in a lower-case column name or use upper-case notation in your dataset
<personnes NAME="toto" pk="1" />
leaving the upper-case column name.
You need to set the following to true
DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES
within your DatabaseConfig object.
See org.dbunit.dataset.NoSuchTableException: Did not find table 'xxx' in schema 'null'
Try to set datatype factory for your databases.
All available factories can be found on this link. http://dbunit.sourceforge.net/apidocs/org/dbunit/dataset/datatype/IDataTypeFactory.html
Choose factory which belongs to your database.
Implement method setUpDatabaseConfig in your test class and set factory.
protected void setUpDatabaseConfig(DatabaseConfig config) {
config.setProperty( DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory() );
}
Related
I am trying to persist a simple class using Spring, with hibernate/JPA and a PostgreSQL database.
The ID column of the table is a UUID, which I want to generate in code, not in the database.
This should be straightforward since hibernate and postgres have good support for UUIDs.
Each time I create a new instance and write it with save(), I get the following error:
o.h.j.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"; SQL statement: INSERT INTO DOODAHS (fieldA, fieldB) VALUES $1, $2) ...
This error indicates that it's expecting the ID column to be auto-populated (with some default value) when a row is inserted.
The class looks like this:
#lombok.Data
#lombok.AllArgsConstructor
#org.springframework.data.relational.core.mapping.Table("doodahs")
public class Doodah {
#org.springframework.data.annotation.Id
#javax.persistence.GeneratedValue(generator = "UUID")
#org.hibernate.annotations.GenericGenerator(name="UUID", strategy = "uuid2")
#javax.persistence.Column(nullable = false, unique = true)
private UUID id;
//... other fields
Things I have tried:
Annotate the field with #javax.persistence.Id (in addition to existing spring Id)
Annotate the field with #org.hibernate.annotations.Type(type = "pg-uuid")
Create the UUID myself - results in Spring complaining that it can't find the row with that id.
Specify strategy = "org.hibernate.id.UUIDGenerator"
Annotate class with #Entity
Replace spring #Id annotation with #javax.persistence.Id
I've seen useful answers here, here and here but none have worked so far.
NB the persistence is being handled by a class which looks like this:
#org.springframework.stereotype.Repository
public interface DoodahRepository extends CrudRepository<Doodah, UUID> ;
The DDL for the table is like this:
CREATE TABLE DOODAHS(id UUID not null, fieldA VARCHAR(10), fieldB VARCHAR(10));
Update
Thanks to Sve Kamenska, with whose help I finally got it working eventually. I ditched the JPA approach - and note that we are using R2DBC, not JDBC, so the answer didn't work straight away. Several sources (here, here, here, here, here and here) indicate that there is no auto Id generation for R2DBC. So you have to add a callback Bean to set your Id manually.
I updated the class as follows:
#Table("doodahs")
public class Doodah {
#org.springframework.data.annotation.Id
private UUID id;
I also added a Bean as follows:
#Bean
BeforeConvertCallback<Doodah> beforeConvertCallback() {
return (d, row, table) -> {
if (d.getId() == null){
d.id = UUID.randomUUID();
}
return Mono.just(d);
};
}
When a new object (with id = null, and isNew = true) is passed to the save() method, the callback method is invoked, and it sets the id.
Initially I tried using BeforeSaveCallback but it was being called too late in the process, resulting in the following exception:
JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "ID"....
Update
There are, at least, 2 types of Spring Data: JPA and JDBC.
The issue happens because you are mixing the 2 of them.
So, in order to fix, there are 2 solutions.
Solution 1 - Use Spring Data JDBC only.
Pom.xml dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
Generate ID.
Spring Data JDBC assumes that ID is generated on database level (like we already figured that out from log). If you try to save an entity with pre-defined id, Spring will assume that it is existing entity and will try to find it in the database and update. That is why you got this error in your attempt #3.
In order to generate UUID, you can:
Leave it to DB (it looks like Postgre allows to do it)
or Fill it in BeforeSaveCallback (more details here https://spring.io/blog/2021/09/09/spring-data-jdbc-how-to-use-custom-id-generation)
#Bean BeforeSaveCallback<Doodah> beforeSaveCallback() {
return (doodah, mutableAggregateChange) -> {
if (doodah.id == null) {
doodah.id = UUID.randomUUID();
}
return doodah;
};
}
Solution 2 - Use Spring Data JPA only
Pom.xml dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Generate ID.
Here you can, actually, use the approach with the UUID auto-generation, like you wanted to do initially
Use javax.persistence #Entity annotation instead of springdata #Table on the class-level
and Use #javax.persistence.Id and #javax.persistence.GeneratedValue with all defaults on id-field.
#javax.persistence.Id
#javax.persistence.GeneratedValue
private UUID id;
Other notes:
Specification of generator and strategy is not required, since it will generate based on the type of the id field (UUID in this case).
Specification of Column(nullable = false, unique = true) is not required either, since putting #Id annotation already assumes these constraints.
Initial answer before update
The main question: how do you save the entity? As id-generation is handled by JPA provider, Hibernate in this case. It is done during save method of em or repository. In order to create entities and ids Hibernate is looking for javax.persistence annotations, while you have Spring-specific, so I am wandering how do you save them.
And another question here: the error you provided INSERT INTO DOODAHS (fieldA, fieldB) VALUES $1, $2 shows that there is no id field in the insert-query at all. Did you just simplified the error-message and removed ID from it? Or this is original error and your code does not even "see" field ID? In that case the issue in not related to the id-generation, but rather is related to the question why your code does not see this field.
I want to have tables located in different database schemas. But unfortunately, I can't achieve this with Spring Boot. Here steps to reproduce it.
Create a new Spring Boot project on http://start.spring.io version 2.0.5 (with derby and PostgreSQL dependencies)
Create simple entity
#Entity
#Table(name = "my_table")
public class MyTable {
#Id Integer id;
}
Add only next property to the application.properties with value 'update' or 'create' (if you try 'create-drop' then you get another error described here: https://github.com/spring-projects/spring-boot/issues/7706#issuecomment-268798059). Now Derby datasource will be used by default.
spring.jpa.hibernate.ddl-auto=create
Run a generated test or main class. Be sure all works fine.
Modify the entity, add attribute schema to the #Table annotation. Now the entity looks like:
#Entity
#Table(name = "my_table", schema = "my_schema")
public class MyTable {
#Id Integer id;
}
Run a test (or main class). This time I get an error while Spring Boot initialization process "java.sql.SQLSyntaxErrorException: Schema 'MY_SCHEMA' does not exist":
Full log listing is available here: https://gist.github.com/asaushkin/8d767c92b2e7025dd359f7be43eefdd6
Check on PostgreSQL. This error reproduces on a PostgreSQL instance too. Without the 'schema' attribute Spring Boot app runs perfect, but as soon as this attribute appears on the #Table annotation the exceptions are thrown.
Full log is here: https://gist.github.com/asaushkin/dd0d677964556bf943c4f013d4785372
My question is: why are schemas not created by Spring Boot?
These options can't resolve this issue too:
spring.jpa.properties.javax.persistence.schema-generation.create-database-schemas=true
spring.jpa.properties.hibernate.hbm2dll.create_namespaces=true
Links
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#configurations-hbmddl
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-jpa-properties
Update (11 March 2019):
I've just check the current behavior of the issue. I wonder, but currently with Derby driver all works fine and the table is created with the specified schema. But in PostgreSQL an error continues exists.
Generated SQL (for PostgreSQL) is:
create table my_schema.my_table (id int4 not null, primary key (id))
Check that are you specifying the database dialect in the application.properties file or not for more check this thread.
Unable to get spring boot to automatically create database schema
I had the same problem with PostgreSQL and JPA (ERROR o.h.e.jdbc.spi.SqlExceptionHelper - ERROR: relation "schema.table" does not exist) and I figured out this solution.
In your entities classes, add escape characters \", between database element´s name. For instance:
Use this form:
#Table(name = "\"USUARIO\"", schema="\"INVENTARIODB\"")
Rather than a typical way
#Table(name = "USUARIO", schema="INVENTARIODB")
The same applies for columns names
#Column(name = "\"ID\"", nullable = false, updatable = false)
private Long id;
Rather than
#Column(name = "ID", nullable = false, updatable = false)
private Long id;
UPDATE:
I discovered the reason that was causing the problem. I used Valentina Studio to create my DB, if I use capital letters (MYTABLE), instead lower-case letters (mytable) to create my tables, I had to use double quotes inside SQL statements. This is because PostgreSQL is case sensitive. If you can´t change your database then use my last solution. Also is a good idea to enable spring.jpa.show-sql=true property, so you can see hibernate´s queries and know what´s going on.
Rename spring.jpa.properties.javax.persistence.schema-generation.create-database-schemas to spring.jpa.properties.javax.persistence.create-database-schemas. In other words, remove '.schema-generation'.
I just had the same problem not with PostgreSQL but H2 - schemas weren't being created. But, as I've discovered, the problem is not with H2 (or, likely, PostgreSQL) but, rather, Hibernate (it deviates from the standard, regarding that nomenclature). That likely means that this solution will work for you too.
I have the "master_seq" defined in 2 schemes. I have a table in schema_2 say table_2. Schema_1 has been provided insert grant on table_2 and its master_seq. The problem is, I am trying to insert a record in table_2 from schema_1 (From the spring boot app I am running) and it is using schema_1's master_seq whereas I want it should use schema_2's master_seq.
In short, I want to use master_seq of the schema where the table is.
Below is the code sample:
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MASTER_SEQ")
#SequenceGenerator(name="MASTER_SEQ",sequenceName="MASTER_SEQ",allocationSize=1)
public Long getId() {
return id;
}
Do I need to use another schema name in code somehow? But schema name keeps on changing as per the different environment.
Thanks in advance
You can try accessing the sequence by appending the schema name to sequence name:
#SequenceGenerator(name="MASTER_SEQ",sequenceName="schema_2.MASTER_SEQ",allocationSize=1)
I know there are plenty of these questions here on SO and also on the net, but all the answers suggest using columnDefinition which is database specific and hence not applicable for me because the system I'm working on needs to run on different databases.
I found this hibernate issue where someone requested this feature for annotations. The issue has been closed saying that another issue will cover that functionality. The second issue apparently added annotation #Generated and also some others, but I couldn't find any documentation on how to define the default column value with those new annotations.
So my question is: Does anyone know how can I define a default column value with annotations (and NOT using columnDefinition)?
Edit: to further clarify my problem: When I add a new not null column, I need Hibernate to update the existing schema (add the new column to the respective table). But since the column is non null, the database cannot create the column without specifying the default value (if there are already some rows in the table). So I need to instruct Hibernate to issue the following DDL statement: ALTER TABLE my_table ADD COLUMN new_column VARCHAR(3) DEFAULT 'def', but it has to be independent of the used database.
I don't think you need any documentation, the java docs are self explaining.
If I understand you correctly you need a way to set a default value for a field. If yes please see the following code snippet.
#Entity
#Table(name = "my_entity")
public class SomeEntity extends BaseEntity {
public static final class MyValueGenerator implements
ValueGenerator<String> {
#Override
public String generateValue(Session session, Object owner) {
return "This is my default name";
}
}
#Basic
#Column(name = "name", insertable = true, updatable = true, nullable = false, length = 255)
// This will add a DDL default
#ColumnDefault("'This is my default name'")
// This will add a runtime default.
#GeneratorType(type = MyValueGenerator.class)
private String name;
// getters and setters
}
Following is working for me.
#ColumnDefault("'0.0'")
#Column(name = "avgRating")
private float avgRating;
I am using hibernate 4.3.5.Final version.
To handle reserve words at Database,I used a property in hibernate
hibernate.globally_quoted_identifiers = true.
And My pojo class having a unique column and it looks like
#Entity
#Table(name="theme1"
,catalog="theme2"
, uniqueConstraints = #UniqueConstraint(columnNames={"name1"})
public class Theme1 implements java.io.Serializable {
#Id #GeneratedValue(strategy=IDENTITY)
private Integer id;
#Column(name="name1", unique=true, nullable=false, length=32)
private String name1;
.....
Then when my SessionFactoryBean is loading it is failing with below error
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (name1) on table theme1: database column 'name1' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1682)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1614)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1450)
In the debugging process,I find issue is because of the property I add (hibernate.globally_quoted_identifiers).
When this property is added,Hibernate will append single quotes to handle reserved words.But while mapping from PhysicalToLogical,it is failed to map 'name1' with name1.Hence I got above error.
Can any one suggest how to handle above two cases(reserve words + UniqueConstraint) at a time.
When hibernate.globally_quoted_identifiers is set, Hibernate expects exact column name.
Check JPA with Hibernate 3.6.8.Final, PostgresSQL 9.1, SQLGrammarException - configuration issue? Weird SQL statement for more information.
So according to this, your column name and Table names in the pojo class need to be quoted.