How to exclude a column during INSERT with dbunit to HSQLDB - java

I export data from MS SQLServer to an xml file, then use that dataset in running unit tests that require database. I use dbunit maven plugin for it.
Unfortunately for me, not all columns in some tables are mapped in my Entity classes.
As an example, say, we have a table called 'member'.
Member table has three columns: memberid, membername, memberrank.
When I do an export, I get all three columns exported.
But in my MemberEntity class, I only map memberid and membername, because I do not need memberrank in my application. So I would have the MemberEntity looking like this:
#Entity
#Table(name = "member")
public class MemberEntity {
#Id
#GeneratedValue()
#Column(name = "memberid", nullable = false)
private Integer memberid;
#Column(name = "membername", nullable = false)
private String membername;
...
}
Then, I try to insert dataset into HSQLDB before a test case:
IDatabaseConnection conn = new DatabaseConnection(((SessionImpl) (entityManager.getDelegate())).connection());
IDataSet dataset = new XmlDataSet(
resourceLoader.getResource("classpath:dataset.xml").getInputStream());
conn.getConfig().setProperty("http://www.dbunit.org/properties/datatypeFactory", new MsSqlDataTypeFactory());
DatabaseOperation.CLEAN_INSERT.execute(conn, dataset);
At this point, I get an exception saying the column MemberRank does not exist. It says something like the following:
org.dbunit.dataset.NoSuchColumnException: MEMBER.MEMBERRANK - (Non-uppercase input column: memberrank) in ColumnNameToIndexes cache map. Note that the map's column names are NOT case sensitive.
When I remove the column from the dataset, all is well. If I add in the memberRank mapping to my Entity class, again, all goes well.
But I cannot add the column mapping into my Entity class. Is there an easy way (other than removing the column and the associated data from the exported dataset manually) of excluding that column from being (attempted to be) added in when I do INSERT?

In hibernate every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as #Transient.
for example,
#Transient
public int counter; //transient property
private String firstname; //persistent property
The methods and fields annotated as #Transient will be ignored by the entity manager.See here for more information.

Maybe this answer comes a little bit late, but I've just run into a similar problem and wrote the following method to solve it (I'm using dbUnit 2.5.0). Hope it helps somebody.
/**
* Generates a new data set with the columns declared in the
* "excludedColumns" map removed.
*
* #param src
* Source data set.
* #param excludedColumns
* Map of table names and column names. Columns in this map are
* removed in the resulting data set.
* #return Data set with the columns declared in the "excludedColumns" map
* removed. Tables that are not specified in the "excludedColumns"
* map are left untouched.
* #throws DataSetException
*/
public static IDataSet filterDataSet(IDataSet src,
Map<String, Set<String>> excludedColumns) throws DataSetException {
if (excludedColumns == null) {
return src;
}
ArrayList<ITable> tables = new ArrayList<ITable>(
src.getTableNames().length);
for (String tableName : src.getTableNames()) {
if (excludedColumns.containsKey(tableName)) {
ITable filteredTable = DefaultColumnFilter
.excludedColumnsTable(
src.getTable(tableName),
excludedColumns.get(tableName).toArray(
new String[0]));
tables.add(filteredTable);
} else {
tables.add(src.getTable(tableName));
}
}
return new DefaultDataSet(tables.toArray(new ITable[0]),
src.isCaseSensitiveTableNames());
}
The core of the method is DefaultColumnFilter. I'm using a commodity static method here, but an instance of DefaultColumnFilter gives a lot of flexibility.
I wonder if there is a more straight forward way of doing this.

Related

Validating field length by making use of Jooq and JPA's #Column annotation

I am trying to validate the data input by the user by making use of JSR 303 validations. One validation that I am trying to implement is to check that the size of the inputted value for each field does not exceed the maximum size of the corresponding column.
In order to map a field to a database column I am making use of JPA's #Column annotation as follows:
#ComplexValidation
public class Person {
#Column(table = "PERSON_DETAILS", name = "FIRST_NAME")
private String firstName;
}
The #ComplexValidation annotation on the Person class, is a JSR 303 custom constraint validator that I am trying to implement, which basically tries to carry out the following steps:
Retreives all fields in the class annotated with #Column annotation
It extracts the table name from the annotation and uses it to load the corresponding JOOQ generated class representing the table
It extracts the field name from the annotation and uses it to load the data type and size for the corresponding column
Is there any way in Jooq where I can retrieve the Jooq Generated class based on the table name? My first attempt can be found below, however it does not work since table(tableName) returns an SQLTable not a TableImpl object:
Column columnAnnotation = field.getDeclaredAnnotation(Column.class);
if (columnAnnotation != null) {
String tableName = columnAnnotation.table();
String fieldName = columnAnnotation.name();
TableField tableField = (TableField) ((TableImpl) table(tableName)).field(fieldName);
int columnLength = tableField.getDataType().length();
if (fieldValue.length() > columnLength) {
constraintValidatorContext
.buildConstraintViolationWithTemplate("exceeded maximum length")
.addPropertyNode(field.getName())
.addConstraintViolation();
}
}
Any other suggestions are welcome :)
Assuming you only have one generated schema (e.g. PUBLIC), you can access tables from there:
Table<?> table = PUBLIC.getTable(tableName);
See Schema.getTable(String)

Hibernate #ElementCollection does not work correctly

I have an Entity in my hibernate 4.2.6 java application as follow:
#Entity
public class Store {
#ElementCollection(fetch=FetchType.EAGER)
List<String> fields = new ArrayList<>();
/* Getter and Setter */
}
Store Table hase one record and For this record, I add two String field. I get all record of table as follow:
return sesseionFactory.getCurrentSession.createCriteria(Store.class).list()
But It return two record!. When I add 3 String filed, it return three record.
Where is problem?

Encapsulating the returning of only some of that data from Hibernate query

I have a POJO that maps to a row in a specific table. The row describes an image in some site and contains data like width, height, url, some sort of status and some more fields. In some legacy code I have a query (in hibernate) that returns the url and the status. This data is encapsulated in a class ImageStatusAndOrigFilename.
I think this is a bad idea because:
What if tomorrow I need to query for some other fields? The name is too coupled to the data.
In the past the only way to get the image width and height was to parse the url. Today we map the width and height in the db and thus I now need to get the image size and status (and I don't care anymore about the original file name). So I need to change this class but can't because it's being used in other places in the code. I wish to get to something more generic that is not coupled to a specific scenario and can be extended when needed.
I'm trying to figure out which data structure to use. Should I use the original POJO that has all the fields but leave some of them null (I don't want to query all of the fields as I don't need all of them in this scenario). Should I create another POJO for this specific query (with a better name of course)?
Any other suggestions are surely welcome as well.
EDIT:
The POJO:
#Entity
#Table(name = "web_image")
public class WebImage {
private long id;
private Document document;
private Integer mediaType;
private Integer width;
private Integer height;
private Date creationDate;
private Date modificationDate;
private String origUrl;
private ImageStatus status;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
public Long getId() {
return id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "document_id")
public Document getDocument() {
return document;
}
public void setDocument(final OBDocument document) {
this.document = document;
}
#Column(name = "width")
public Integer getWidth() {
return width;
}
public void setWidth(final Integer width) {
this.width = width;
}
// Other getters and setters for the rest of the private fields
}
The query:
SELECT b.document_id , b.status , b.orig_file_id, a.min_id as id FROM web_image b,
( SELECT x.document_id, MAX(x.id) max_id, MIN(x.id) min_id
FROM web_image x
WHERE x.document_id in ( :docs ) GROUP BY x.document_id) a
WHERE a.max_id = b.id
What about this:
#MappedSuperclass
public class ImageStatusAndOrigFilename {
...
}
#Entity
public class WebImage extends ImageStatusAndOrigFilename {
...
}
Now you have both classes, the old one is not an entity, but its clients don't know anything about it, and all fetching and persistings are on WebImage class, but you can query for ImageStatusAndOrigFilename.
If it is really necessary to avoid loading unused columns - and you would need to profile to determine if any saving is actually worth it - then a simple solution is simply to write a query using a JPA Contructor Expression.
JPQL:
http://docs.oracle.com/html/E24396_01/ejb3_langref.html#ejb3_langref_constructor
Criteria API:
http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/querycriteria.html#querycriteria-typedquery-multiselect
Problem with these is you need to add a constructor for each set of properties.
Hibernate specific option - no Constructors required:
See Transformers.aliasToBean:
Java - Hibernate criteria.setResultTransformer() initializes model fields with default values
For all of these options you can use some other DTO or use your existing Entity i.e. return an unmanaged instance.
Lazy Loading
You can also lazy load fields however that required byte code manipulation:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-fetching-lazyproperties
Also note the comments:
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note
that this is mostly a marketing feature; optimizing row reads is much
more important than optimization of column reads. However, only
loading some properties of a class could be useful in extreme cases.

Native Query (JPA ) not reset and return the same old result

I have a native sql query as the following :
for (init i=0; i<=2 ; i++) {
String sql = "Select * from accounts where id = ?";
Query query = em.createNativeQuery(sql,AccountBean.class);
query.setParameter(1, i );
AccountBean accountBean = (AccountBean)query.getSingleResult();
}
For the first loop it works correctly but any loop after the first one returns the same result as the first one , i debug it, the parameter changed , it works correctly if i change
Query query = em.createNativeQuery(sql,AccountBean.class);
to
Query query = em.createNativeQuery(queryString);
Regards
Wish79
Every JPA entity must have a primary key. Your JPA entities may not properly reflect the primary key, if any, on the database table.
I ran into the same problem. In my model class I had only one class variable annotated with #Id. However, that was not an accurate reflection of the table itself, which has a composite primary key. Thus, my query results returned the correct number of rows, but each confoundingly contained the same values, even though the actual data was different in the db. For example, this query:
Query query = entityManager.createQuery
("SELECT tbl FROM Tbl tbl WHERE tbl.id = 100
and tbl.code in ('A','B','C')");
...returned 10 rows, each showing a code of 'A'. But in actuality 9 of those 10 rows had a different code value ('B' or 'C'). It seemed as if the results were being cached and/or the tbl.code predicate was ignored. (That happened whether I used JPQL or Native SQL.) Very confusing.
To fix this I added an additional #Id annotation to my model to reflect the composite primary key:
#Id
#Column(name = "Code")
public String getCode() {
return this.code;
}
Now the query returns the data correctly and the code select criteria is no longer effectively ignored.
Edit: Although the above worked for me, on further research it seems a better approach to configure a separate JPA Entity composite primary key class. See http://docs.oracle.com/cd/E16439_01/doc.1013/e13981/cmp30cfg001.htm.
For example, here's an Entity class with an embedded primary key (see #EmbeddedId):
/**
* The persistent class for the SOME_TABLE database table.
*/
#Entity
#Table(name = "SOME_TABLE")
public class SomeTable implements Serializable {
#EmbeddedId
private SomeTablePk id;
#Column(name = "NUMBER_HRS")
private BigDecimal numberHrs;
...
...and here's the composite primary key class (see #Embeddable):
#Embeddable
public class SomeTablePk implements Serializable {
#Column(name = "SOME_ID")
private String someId;
#Column(name = "ANOTHER_ID")
private BigDecimal anotherId;
public String getSomeId() {
return someId;
}
...

Need to know if each field has changed, how should I model this in Hibernate

So I have a class with three fields that maps to a table using hibernate
Class Widget
{
String field1;
String field2;
String field3;
}
On application startup a number of instances these widgets will be added to the database from an external files, but when I exit the application I need to know which (if any) of these fields have been changed by the user since the application was started, so the changes can be saved back to the files. I also need to store the original value for logging purposes.
I can't work whether I need a status field in the table or whether there is already a way of doing this using Hibernate/Database.
EDIT:A good solution to the program was given below . however the main reason I am using Hibernate is to reduce memory consumption so storing the original values when changed is not a good solution for me , I want everthing stored in the database. So I have create this new question How do I store a copy of each entity I add to database in Hibernate
Given an entity like the following you can track changes on one of it's field (while preserving its original value too).
#Entity
#Table(schema = "test", name = "test")
public final class Test {
private static final int ORIGINAL = 0;
private static final int CURRENT = 1;
private Integer id;
// holds the original and current state of the field
private final AtomicReferenceArray<String> field = new AtomicReferenceArray<>(2);
#Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Transient
public String getOriginalField() {
return field.get(ORIGINAL);
}
#Basic
public String getField() {
return field.get(CURRENT);
}
public void setField(String field) {
this.field.compareAndSet(ORIGINAL, null, field);
this.field.set(CURRENT, field);
}
#PreUpdate
public void preUpdate() {
System.out.format("Original: %s, New: %s\n", getOriginalField(), getField());
}
...
}
If there is a single row in a database like this:
id: 1
field: a
version: 2011-12-02 11:24:00
before the field gets updated (say, from a to b) you'll get the following output.
Original: d, New: b
The original value gets preserved even if the the entity is updated multiple times and both state can be accessed through the corresponding getters (getField and getOriginalField—you can get more creative than me in the naming :).
This way, you can spare yourself from creating version columns in your database and also can hide the implementation details from clients.
Instead of an AtomicReferenceArray you could use arrays, lists, etc, to track all changes like this way.
The #PreUpdate isn't necessary of course, but this way you can be notified of changes in the entity's state and atomically save the updated fields into file. There more annotations like these: see the documentation for javax.persistence for other annotation types.
If you are using MySql then you can get table's last update time from information_schema database like
SELECT UPDATE_TIME FROM `information_schema`.`tables`
WHERE TABLE_SCHEMA = 'dbName' AND TABLE_NAME = 'tableName'
Or else simple solution will be to add a column for update time stamp. By this you can even monitor which particular row has been updated.
If you need to synchronize with files as soon as you save into database, You can use the Hibernate event mechanism to intercept any save to database and save it to file, here's a sample doing that.

Categories

Resources