JPA EntityManager createQuery() error - java

This is failing:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type != :type1");
query.setParameter("type1", TypeActionCommercialeEnum.NIP);
return (List<TypeActionCommerciale>) query.list();
}
exception:
Hibernate: select typeaction0_.id as id1_102_, typeaction0_.libelle as
libelle3_102_, typeaction0_.code as code4_102_, typeaction0_.type as
type5_102_ from apex.typeActionCommerciale typeaction0_ where
typeaction0_.type<>?
ERROR
org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions(SqlExceptionHelper.java:129)
No value specified for parameter 1 org.hibernate.exception.DataException: could not extract ResultSet at
i use setProperties but i have the same error:
public List<TypeActionCommerciale> requestTypeActionCommercialeSansNip() throws PersistenceException {
Query query = createQuery("from TypeActionCommercialeImpl where type <> :type1");
final Map<String, Object> properties = new HashMap<>();
properties.put("type1", TypeActionCommercialeEnum.NIP);
query.setProperties(properties);
return (List<TypeActionCommerciale>) query.list();
}

The problem is here query.setParameter("type1", TypeActionCommercialeEnum.NIP);
The enum type is not defined in hibernate so you must store the name of enum and use it for the query (the easy way) then use:
query.setString("type1", TypeActionCommercialeEnum.NIP.name());
To use enum directly (the hard way) you must implement your CustomUserType . You can find here how https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch06.html#types-custom
The main advantages of use CustomUserType are:
you can store into DB an integer (that is more smaller) instead of a string that represents the enum.
Delegate the parsing to hibernate during storing and retrieving of object.
You can use the enum directly into the query (like you are trying to do)

Try to use <> instead of != like this:
"from TypeActionCommercialeImpl where type <> :type1"

i resolve my pb i have a class public class EnumUserType<E extends Enum<E>> implements UserType and i implement this method:
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
String name = rs.getString(names[0]);
Object result = null;
if (!rs.wasNull()) {
result = Enum.valueOf(clazz, name);
}
return result;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (null == value) {
st.setNull(index, Types.VARCHAR);
} else {
st.setString(index, ((Enum<E>) value).name());
}
}

Related

Implementing generic DAO interface, return type of class implementing it must be cast to type T? why

I am implementing a DAO for my first full stack java program and I am a bit confused when trying to implement this generic DAO interface. See below
public interface IDataAccessDAO<T> {
List<T> selectAll();
T selectSingle(int id);
void insert(T t);
void update(T t, String[] params);
void delete(T t);
}
****************
#Override
public List<T> selectAll() {
List<Admin> allAdmins = new ArrayList<>();
try {
//Create a connection to DB and prepare select statement
Connection myConnect = this.getConnection()
PreparedStatement ps = myConnect.prepareStatement(selectAllAdmin);
//Store query result to build list of admins
ResultSet queryResult = ps.executeQuery();
while(queryResult.next()){
int id = queryResult.getInt("adminId");
String name = queryResult.getString("name");
String email = queryResult.getString("email");
String userName = queryResult.getString("userName");
String password = queryResult.getString("password");
Admin admin = new Admin(id, name, userName, password, email);
allAdmins.add(admin);
return (List<T>) allAdmins;
}
My question is, why can I not just return allAmins instead of casting it to a generic. I thought the point of generics was to create flexibility in the return types of a class?

How to use the IN operator in conjunction with the JDBI?

Good afternoon.
I'm trying to bind a list with strings to the query that the IN operator uses.
im use Oracle.
I did following the example that was described by the link:
How to use IN operator with JDBI?
List<String> ms = new ArrayList();
ms.add("Novosibirsk");
ms.add("Perm");
public interface CityDAO {
#RegisterMapper(CitiesMapper.class)
#SqlQuery("SELECT *
FROM Universities
WHERE Location IN (:cities)")
List<cities> getItems(#Bind("cities") List<String> cities);}
}
I created a ListArgumentFactory
public class ListArgumentFactory implements ArgumentFactory<List> {
#Override
public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
return value instanceof List;
}
#Override
public Argument build(Class<?> expectedType, final List value, StatementContext ctx) {
return new Argument() {
#Override
public void apply(int position, PreparedStatement statement, StatementContext ctx) throws SQLException {
String type = null;
if(value.get(0).getClass() == String.class){
type = "varchar";
} else if(value.get(0).getClass() == Integer.class){
// For integer and so on...
} else {
// throw error.. type not handled
}
Array array = ctx.getConnection().createArrayOf(type, value.toArray());
statement.setArray(position, array);
}
};
}
}
I registered the factory
public class DBI extends AbstractModule {
private DBI dbi;
#Override
protected void configure() {
this.dbi = new DBI(provideConfig().url());
this.dbi.registerArgumentFactory(new ListArgumentFactory());
}
}
But when I make a request I get an exception
org.skife.jdbi.v2.exceptions.UnableToCreateStatementException: Exception while binding 'cities' [statement:"SELECT * FROM Universities WHERE Location IN (:cities)", arguments:{ positional:{}, named {cities:factory.ListArgumentFactory$1#6788168c}, finder:[]}]
Help me figure out what I'm doing wrong
According to the JDBI documentation, achieving something like that using Oracle could be quite complex so might be a better idea to use the first approach described (UseStringTemplate3StatementLocator):
Oracle supports something similar, but you need to use Oracle specific APIs and oracle.sql.ARRAY instances. In the Oracle case you have to pre-declare the array type in the database first, and as it stores the array in the database, free it after the call.
Having said that, there is a simple approach that can be used to make this work in Oracle which is to join the elements in the list with a comma. I have modified the ListArgumentFactory using the Java 8 String.join method:
public class ListArgumentFactory implements ArgumentFactory<List> {
#Override
public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
return value instanceof List;
}
#Override
public Argument build(Class<?> expectedType, final List value, StatementContext ctx) {
return new Argument() {
#Override
public void apply(int position, PreparedStatement statement, StatementContext ctx) throws SQLException {
statement.setString(position, String.join(",", value));
}
};
}
}
I have tried the approach described in the JDBI documentation to use oracle.sql.ARRAY and a custom TYPE in the Oracle DB but was not successful for me.

Hibernate Envers : How to check if a field has changed between two revisions?

I am using Hibernate envers to audit entities in my application. I have separate _audit tables for each entity and then in those tables I have _mod boolean column to indicate if the field has changed or not.
But, I m not getting how to use that column in queries or even how do I get this data in the code?
e.g. following code gives list of audited persons. How do I check which data has changed?
List person = getAuditReader().createQuery()
.forEntitiesAtRevision(Person.class, 12)
.getResultList();
You can loop through all the fields of the given entity and find out if that field is changed or not.
Sample code snippet to loop through all the fields of Person entity for revision number 12 and find value of fields which are updated.
public void processFields() throws Exception {
for (Field field : Person.class.getDeclaredFields()) {
final Person changedEntity = fetchEntityForRevisionWhenPropertyChanged(Person.class, 12, field.getName());
if (changedEntity != null) {
// Updated field value for the field. This will list all fields which are changed in given revision.
final Object fieldValue = getField(changedEntity, field.getName());
}
}
}
private <T> Object getField(final T object, final String fieldName) throws Exception {
return new PropertyDescriptor(fieldName, object.getClass()).getReadMethod().invoke(object);
}
private <T> T fetchEntityForRevisionWhenPropertyChanged(final Class<T> entityClass, final int revisionNumber, final String propertyName) {
final List<T> resultList = getAuditReader()
.createQuery()
.forEntitiesModifiedAtRevision(entityClass, revisionNumber)
.add(AuditEntity.property(propertyName).hasChanged())
.addOrder(AuditEntity.id().asc())
.getResultList();
if (CollectionUtils.isNotEmpty(resultList)) {
return resultList.get(0);
} else {
return null;
}
}
In any case if you want to find previous revison of your entity for comparision, you can use following method:
public T getPreviousVersion(final Class<T> entityClass, final Long entityId, final Long currentRevisionNumber) {
final AuditReader reader = AuditReaderFactory.get(entityManager);
final Number previousRevision = (Number) reader.createQuery()
.forRevisionsOfEntity(entityClass, false, true)
.addProjection(AuditEntity.revisionNumber().max())
.add(AuditEntity.id().eq(entityId))
.add(AuditEntity.revisionNumber().lt(currentRevisionNumber))
.getSingleResult();
return Optional.ofNullable(previousRevision)
.map(previousRev -> reader.find(entityClass, entityId, previousRev))
.orElse(null);
}
You can try to use Hibernate interceptors. Here is good article about interceptors.
With interceptor you can create a callback which would be executed on entity updates/creation etc. It would be smth like this:
public class EntityInterceptor extends EmptyInterceptor {
#Override
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if ( entity instanceof YourEntity ) {
//do update stuff
return true;
}
return false;
}
}
You can compare currentState with previousState to collect information about entity changes and persist it to other tables.

Java SQLData - Cast to user object with a list/array?

I'm learning on how to use SQLData and having an issue with casting back to my object.
My Oracle Types looks something like this:
CREATE OR REPLACE TYPE activities_t AS OBJECT
(
list activity_list_t;
);
CREATE OR REPLACE TYPE activity_list_t AS TABLE OF activity_t;
CREATE OR REPLACE TYPE activity_t AS OBJECT
(
startDate DATE;
endDate DATE;
);
And my Java looks like this:
public class Activities implements SQLData {
private String sqlType = "ACTIVITIES_T";
List<Activity> list;
// must have default ctor!
public Activities() {
}
public String getSQLTypeName() throws SQLException
{
return sqlType;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public void readSQL(SQLInput stream, String typeName) throws SQLException
{
Array a = stream.readArray();
// :(
}
public void writeSQL(SQLOutput stream) throws SQLException
{
// stream.writeArray(this.list);
}
}
I've tried a few things in readSQL but I am not having much success - what am I missing?
I am calling a PLSQL stored procedure which has an OUT parameter of "activities_t" using JDBC:
Map map = connection.getTypeMap();
map.put("ACTIVITIES_T", Class.forName("Activities"));
connection.setTypeMap(map);
callableStatement = connection.prepareCall("{call GET_ACTIVITIES(?)}");
callableStatement.execute();
Thanks!
Steve
(most of the above is from memory as the code is at work...)
You'll need to add a type mapping for the type ACTIVITY_T as well as the one for ACTIVITIES_T. It's not clear from your question whether you've already done this.
Let's assume you've done this and created a class called Activity which implements SQLData as well. Once you've done that, the following should suffice to read the activity list within Activities:
public void readSQL(SQLInput stream, String typeName) throws SQLException {
Array array = stream.readArray();
this.list = new ArrayList<Activity>();
for (Object obj : (Object[])array.getArray()) {
list.add((Activity)obj);
}
}
Tips:
JDBC APIs are case-sensitive with regard to type names; you will see a Unable to resolve type error if your type name does not exactly match. Oracle will uppercase your type name unless you double-quoted the name in its create statement.
You may need to specify SCHEMA.TYPE_NAME if the type isn't in your default schema.
Remember to grant execute on types if the user you are connecting with is not the owner.
If you have execute on the package, but not the type, getArray() will throw an exception when it tries to look for type metadata.
getArray()
My solution is essentially the same as Luke's. However, I needed to provide a type mapping when getting the array: array.getArray(typeMap)
You can also set a default type map on the Connection, but this didn't work for me.
When calling getArray() you get an array of the object type, i.e. the SQLData implementation you created that represents activity_t
Here is a generic function you might find useful:
public static <T> List<T> listFromArray(Array array, Class<T> typeClass) throws SQLException {
if (array == null) {
return Collections.emptyList();
}
// Java does not allow casting Object[] to T[]
final Object[] objectArray = (Object[]) array.getArray(getTypeMap());
List<T> list = new ArrayList<>(objectArray.length);
for (Object o : objectArray) {
list.add(typeClass.cast(o));
}
return list;
}
writeArray()
Figuring out how to write an array was frustrating, Oracle APIs require a Connection to create an Array, but you don't have an obvious Connection in the context of writeSQL(SQLOutput sqlOutput). Fortunately, this blog has a trick/hack to get the OracleConnection, which I've used here.
When you create an array with createOracleArray() you specify the list type for the type name, NOT the object type. i.e. activity_list_t
Here's a generic function for writing arrays. In your case, listType would be "activity_list_t" and you would pass in a List<Activity>
public static <T> void writeArrayFromList(SQLOutput sqlOutput, String listType, #Nullable List<T> list) throws SQLException {
final OracleSQLOutput out = (OracleSQLOutput) sqlOutput;
OracleConnection conn = (OracleConnection) out.getSTRUCT().getJavaSqlConnection();
conn.setTypeMap(getTypeMap()); // not needed?
if (list == null) {
list = Collections.emptyList();
}
final Array array = conn.createOracleArray(listType, list.toArray());
out.writeArray(array);
}
Note: at one point I thought setTypeMap was required, but now when I remove that line my code still works, so I'm not sure if it's necessary.

How to prepare an argument of type List<Entry<? extends Class<?>, ?>>

The objective is to write a convenience method that return a ResultSet from a JDBC query with a simple client-side invocation form.
I have written something like this:
public class JdbcQueryManager {
public static ResultSet executePreparedStatementWithParameters(
Connection jdbcConnection, String sqlQuery,
Map.Entry<? extends Class<?>, ?>... sqlQueryParameters)
throws JdbcQueryFailureException {
return executePreparedStatementWithParameters(jdbcConnection, sqlQuery,
Arrays.asList(sqlQueryParameters), ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
private static ResultSet executePreparedStatementWithParameters(
Connection jdbcConnection, String sqlQuery,
List<Map.Entry<? extends Class<?>, ?>> sqlQueryParameters,
int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws JdbcQueryFailureException {
try {
PreparedStatement preparedStatement =
jdbcConnection.prepareStatement(sqlQuery, resultSetType,
resultSetConcurrency, resultSetHoldability);
for (int i = 0; i < sqlQueryParameters.size(); i++) {
int sqlQueryParameterIndex = i + 1; // SQL parameters are 1-based
Entry<? extends Class<?>, ?> sqlQueryParameter =
sqlQueryParameters.get(i);
Class<?> sqlQueryParameterClass = sqlQueryParameter.getKey();
if (sqlQueryParameterClass == Integer.class) {
int sqlQueryParameterIntegerValue =
(Integer) sqlQueryParameter.getValue();
preparedStatement.setInt(sqlQueryParameterIndex,
sqlQueryParameterIntegerValue);
} else if (sqlQueryParameterClass == String.class) {
String sqlQueryParameterStringValue =
(String) sqlQueryParameter.getValue();
preparedStatement.setString(sqlQueryParameterIndex,
sqlQueryParameterStringValue);
// TODO: accept other types, not just String and Integer
} else {
throw new JdbcQueryFailureException(new IllegalArgumentException(
sqlQueryParameterClass.getName()));
}
}
ResultSet resultSet = preparedStatement.executeQuery();
return resultSet;
} catch (SQLException sqlException) {
throw new JdbcQueryFailureException(sqlException);
}
}
}
using this convenience class:
public class QueryParameter<T> extends AbstractMap.SimpleEntry<Class<T>, T> {
#SuppressWarnings("unchecked")
public QueryParameter(T parameterValue) {
super((Class<T>) parameterValue.getClass(), parameterValue);
}
}
to be able to execute a JDBC SQL statement like this:
ResultSet resultSet =
JdbcQueryManager.executePreparedStatementWithParameters(jdbcConnection,
sqlQuery, new QueryParameter<String>("AnswerRequest"),
new QueryParameter<Integer>(42));
... how can I make it better?
Specifically, my perplexities lie in the use of this seemingly complicated, possibly unneeded form:
List<Map.Entry<? extends Class<?>, ?>>
There is no value in passing in a list of Map.Entry<? extends Class<?>, ?> - you are attempting to tell your method what class each parameter is. Don't do this!!!
Contrary to popular belief, you don't need to use the various typed preparedStatement.setXXX() methods if you're using "basic" java objects (wrapped primitives and Dates), just use preparedStatement.setObject(index, object) and the jdbc driver will figure out what to do!
The only time you need to use a typed setter is if your object is not one of the "basic" types. If you really need this, then just use instanceof to check each parameter, then you'd write some code to maybe extract a String value to use, but you could still call preparedStatement.setObject(index, object) with that String.
I have written something like this myself and I simply used:
public static ResultSet executePreparedStatementWithParameters(
Connection jdbcConnection, String sqlQuery, Object... parameters)
and it works just fine.

Categories