Store Kotlin inline class to MongoDB with Spring Data - mongodb

I'm trying to store a Kotlin's (v1.3.61) inline class to a MongoDB using Spring Data MongoDB (2.2.3-RELEASE), with no luck so far. This is the set up:
inline class UserId(#NotBlank val id: String)
and
#Document(collection = "data")
class Data(
#Field("uid")
val userId: UserId
)
Spring throws the following exception when creating its beans:
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1 at
org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers.buildPreferredConstructor(PreferredConstructorDiscoverer.java:221)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers.access$200(PreferredConstructorDiscoverer.java:89)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers$2.lambda$discover$0(PreferredConstructorDiscoverer.java:161)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
~[na:1.8.0_171] at
java.util.Spliterators$ArraySpliterator.tryAdvance(Spliterators.java:958)
~[na:1.8.0_171] at
java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126)
~[na:1.8.0_171] at
java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498)
~[na:1.8.0_171] at
java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485)
~[na:1.8.0_171] at
java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
~[na:1.8.0_171] at
java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:152)
~[na:1.8.0_171] at
java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
~[na:1.8.0_171] at
java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
~[na:1.8.0_171] at
org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers$2.discover(PreferredConstructorDiscoverer.java:164)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.model.PreferredConstructorDiscoverer.discover(PreferredConstructorDiscoverer.java:77)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.model.BasicPersistentEntity.(BasicPersistentEntity.java:105)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity.(BasicMongoPersistentEntity.java:74)
~[spring-data-mongodb-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mongodb.core.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:91)
~[spring-data-mongodb-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mongodb.core.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:39)
~[spring-data-mongodb-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:357)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:323)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_171] at
org.springframework.data.mapping.context.AbstractMappingContext.initialize(AbstractMappingContext.java:452)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.data.mapping.context.AbstractMappingContext.afterPropertiesSet(AbstractMappingContext.java:444)
~[spring-data-commons-2.2.3.RELEASE.jar:2.2.3.RELEASE] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE] ... 151 common frames
omitted
The stacktrace is quite obscure, but the exception occurs in the PreferredConstructorDiscoverer and the userId is part of the constructor, so the issue might be located there.
Next thing was checking the byte code of the Data class:
// ================net/test/Data.class
================= // class version 50.0 (50) // access flags 0x31
public final class net/test/Data {
#Lorg/springframework/data/mongodb/core/mapping/Document;(collection="data")
// access flags 0x12 private final Ljava/lang/String; userId #Lorg/springframework/data/mongodb/core/mapping/Field;(value="uid") #Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x11 public final getUserId()Ljava/lang/String; #Lorg/jetbrains/annotations/NotNull;() // invisible L0
LINENUMBER 10 L0
ALOAD 0
GETFIELD net/test/Data.userId : Ljava/lang/String;
ARETURN L1
LOCALVARIABLE this Lnet/test/Data; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x2 private <init>(Ljava/lang/String;)V L0
LINENUMBER 7 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
ALOAD 0
ALOAD 1
PUTFIELD net/test/Data.userId : Ljava/lang/String;
RETURN L1
LOCALVARIABLE this Lnet/test/Data; L0 L1 0
LOCALVARIABLE userId Ljava/lang/String; L0 L1 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1001 public synthetic <init>(Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
// annotable parameter count: 2 (visible)
// annotable parameter count: 2 (invisible) L0
LINENUMBER 7 L0
ALOAD 0
ALOAD 1
INVOKESPECIAL net/test/Data.<init> (Ljava/lang/String;)V
RETURN L1
LOCALVARIABLE this Lnet/test/Data; L0 L1 0
LOCALVARIABLE userId Ljava/lang/String; L0 L1 1
LOCALVARIABLE $constructor_marker Lkotlin/jvm/internal/DefaultConstructorMarker; L0 L1 2
MAXSTACK = 2
MAXLOCALS = 3
#Lkotlin/Metadata;(mv={1, 1, 16}, bv={1, 0, 3}, k=1, d1={"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0008\u0005\u0008\u0007\u0018\u00002\u00020\u0001B\u0010\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u00f8\u0001\u0000\u00a2\u0006\u0002\u0010\u0004R\u001b\u0010\u0002\u001a\u00020\u00038\u0006X\u0087\u0004\u00f8\u0001\u0000\u00a2\u0006\n\n\u0002\u0010\u0007\u001a\u0004\u0008\u0005\u0010\u0006\u0082\u0002\u0004\n\u0002\u0008\u0019\u00a8\u0006\u0008"}, d2={"Lnet/test/Data;", "", "userId", "Lnet/test/UserId;", "(Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V", "getUserId", "()Ljava/lang/String;", "Ljava/lang/String;", "core"}) // compiled from: Data.kt }
// ================META-INF/core.kotlin_module =================
and this is the relevant line 221 of the PreferredConstructorDiscoverer from the Spring source:
String name = parameterNames == null ? null : parameterNames[i];
I'm a total noob in understand byte code, but one or the other might see the issue right away.
Something else I've tried was implementing two custom converters of type org.springframework.core.convert.converter.Converter<UserId,String> and vice versa. However, the bean instantiation takes place before any conversion can occur, so this seems to be irrelevant.
I'm aware that inline classes are still experimental in Kotlin 1.3, but maybe the actual issue lies somewhere else. Does anybody have an idea?
By the way, when changing the type from UserId to String everything works fine.

I'm using a workaround for this, by introducing a private field.
inline class UserId(#NotBlank val id: String = UUID.randomUUID().toString())
#Document(collection = "data")
data class Data(
#Transient val userId: UserId = UserId()
) {
#Id private val idData = userId.id
}
But with this approach you should switch from extending CRUD-Interfaces to implement the basic operations by yourself (otherwise the ID-class makes no sense):
// introduce some extension functions for easier use with mongo-template
suspend inline fun <T> ReactiveMongoTemplate.saveAndAwait(entity: T) : T =
save(entity).awaitSingle()
suspend inline fun <reified T> ReactiveMongoTemplate.findById(id: Any) : T =
findById(id, T::class.java).awaitSingle()
#ExperimentalCoroutinesApi
#Repository
internal class DataRepository(
private val template: ReactiveMongoTemplate
) {
suspend fun save(order: Order) : Order = template.saveAndAwait(order)
suspend fun findById(orderId: OrderId): Order = template.findById(orderId.id)
}
If its worth the overhead?
Pro:
Typed Ids
in Mongo the id-column is still a primitive
Cons:
Repositories get more complex
If DATACMNS-1517 is fixed sometime in the future, adaption should be relatively easy (mostly the implementation of repositories is removed in favour to extended interfaces).

Have you seen Spring data JPA throwing java.lang.ArrayIndexOutOfBoundsException: X [Kotlin]? Looks like there is no good workaround for this issue. Here is the issue for Spring Data https://jira.spring.io/browse/DATACMNS-1517.

Related

DB2 not accepting UUID

I am using UUID datatype for generating alpha numeric primary key. Following is the code:
public class Example {
private UUID id;
#Id
#Column(name = "ID", nullable = false, length = 36)
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
}
The Java code is generating the uuid something like - c70e22b4-d2a8-11ea-87d0-0242ac130003.
But on saving the values in a DB2 table, in UUID column(which is of VARCHAR(25) type in DB2), I am getting values as in table as:
<failed to load>
com.ibm.db2.jcc.am.SqlException: [jcc][t4][1065][12306][4.23.42] Caught java.io.CharConversionException. See attached Throwable for details. ERRORCODE=-4220, SQLSTATE=null
at com.ibm.db2.jcc.am.ld.a(ld.java:794)
at com.ibm.db2.jcc.am.ld.a(ld.java:66)
at com.ibm.db2.jcc.am.ld.a(ld.java:125)
at com.ibm.db2.jcc.am.mc.a(mc.java:2963)
at com.ibm.db2.jcc.am.mc.p(mc.java:575)
at com.ibm.db2.jcc.am.mc.P(mc.java:1656)
at com.ibm.db2.jcc.am.ResultSet.getStringX(ResultSet.java:1223)
at com.ibm.db2.jcc.am.ResultSet.getString(ResultSet.java:1192)
at com.intellij.database.remote.jdbc.impl.RemoteResultSetImpl.tryGetObject(RemoteResultSetImpl.java:1289)
at com.intellij.database.remote.jdbc.impl.RemoteResultSetImpl.getObject(RemoteResultSetImpl.java:1272)
at com.intellij.database.remote.jdbc.impl.RemoteResultSetImpl.getCurrentRow(RemoteResultSetImpl.java:1249)
at com.intellij.database.remote.jdbc.impl.RemoteResultSetImpl.getObjects(RemoteResultSetImpl.java:1229)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:346)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:568)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:826)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:683)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:682)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
at com.ibm.db2.jcc.am.r.a(r.java:52)
at com.ibm.db2.jcc.am.mc.a(mc.java:2952)
... 25 more
Looks like I can not map UUID to Varchar, but not able to find the alternative.
Any help would be appreciated.
Not sure where you got a length of 25 from....
c70e22b4-d2a8-11ea-87d0-0242ac130003 is 36 characters...
if you want to store the binary value, you only need 16 bytes, but they must for CHAR(16) FOR BIT DATA
You can convert text UUID stings in Db2 to binary with the [VARCHAR_BIT_FORMAT][1] function
CREATE TABLE UUID(U BINARY(16) NOT NULL PRIMARY KEY)
;
INSERT INTO UUID
VALUES VARCHAR_BIT_FORMAT(
'c70e22b4-d2a8-11ea-87d0-0242ac130003'
, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
)

IndexOutBoundException While Copying Image File [duplicate]

I have written the following scala code to download a file . The file gets downloaded correctly but also an exception is thrown. The code is as follows :
var out:OutputStream = null
var in:InputStream = null
try {
var url:URL = null
url = new URL("http://somehost.com/file.doc")
val uc = url.openConnection()
val connection = uc.asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
val buffer:Array[Byte] = new Array[Byte](1024)
var numRead:Int = 0
in = connection.getInputStream()
var localFileName="test.doc"
out = new BufferedOutputStream(new FileOutputStream(localFileName))
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer,0,numRead);
}
}
catch {
case e:Exception => println(e.printStackTrace())
}
out.close()
in.close()
The file gets downloaded but the following exception is thrown :
java.lang.IndexOutOfBoundsException
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:260)
at java.io.BufferedOutputStream.write(BufferedOutputStream.java:105)
at TestDownload$.main(TestDownload.scala:34)
at TestDownload.main(TestDownload.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)
()
Why could this be happening and any way to fix it ?
Please Help
Thank You
Scala returns type Unit, not the type of the value being assigned, with an assignment statement. So
numRead = in.read(buffer)
never returns -1; it doesn't even return an integer. You can write
while( { numRead = in.read(buffer); numRead != -1 } ) out.write(buffer, 0, numRead)
or you can go for a more functional style with
Iterator.continually(in.read(buffer)).takeWhile(_ != -1).foreach(n => out.write(buffer,0,n))
Personally, I prefer the former since it's shorter (and relies less on iterator evaluation happening the way "it ought to").
An alternative option is to use the system commands which is much cleaner and faster from what I can tell.
import sys.process._
import java.net.URL
import java.io.File
new URL("http://somehost.com/file.doc") #> new File("test.doc") !!

Executing multiline query does not work with table alias where single-line query does

This is my first time using ucanaccess since I just found out that my previous version of Eclipse would not work with Java 1.8 so I can get to my Access DB. I am familiar with Java programming and RDBMS (currently use Oracle) so I don't understand why I'm getting an error with my code.
This is the code I use to connect to the DB (variable dbFile is a File object used to store the Access DB path):
try
{
String urlDB = "jdbc:ucanaccess://"+ dbFile.getAbsolutePath();
Connection conn = DriverManager.getConnection(urlDB, "", "");
System.out.println("Connection Successful\n");
Statement test = conn.createStatement();
/* ResultSet rs = test.executeQuery("SELECT e.* "
+ "FROM Employees e"
+ "WHERE e.EmployeeNo LIKE 'H%'"
+ "ORDER BY e.LastName");
System.out.println(); //spacing
//retrieve column data
while(rs.next())
{
//can get data by column name instead of ID
String id = rs.getString("ID");
String fName = rs.getString("FirstName");
String lName = rs.getString("LastName");
String dName = rs.getString("DisplayName");
System.out.println(id + "\t" + fName + "\t" + lName + "\t");
}
*/
ResultSet rs = test.executeQuery("SELECT e.* FROM Employees e WHERE e.EmployeeNo LIKE '1%' ORDER BY e.LastName");
//get table information (i.e. column names)
ResultSetMetaData rsmd = rs.getMetaData();
int dbColumnCount = rsmd.getColumnCount();
/* //db columns starts at 1 not 0
for (int count = 1; count <= dbColumnCount; count++)
{
System.out.print(rsmd.getColumnName(count) + "\t");
}
*/
System.out.format("%5s%10s%15s%20s%n", rsmd.getColumnName(1), rsmd.getColumnName(2),
rsmd.getColumnName(3), rsmd.getColumnName(4));
//System.out.println();
while(rs.next())
{
//can get data by column name instead of ID
String id = rs.getString("ID");
String fName = rs.getString("FirstName");
String lName = rs.getString("LastName");
String dName = rs.getString("DisplayName");
//System.out.println(id + "\t" + fName + "\t" + lName + "\t");
System.out.format("%5s%10s%15s%20s%n", id, fName, lName, dName);
}
}
catch (Exception e)
{
e.printStackTrace();
System.err.println("Could not connect to the Database");
System.err.println(e.getMessage());
}
}
The first call to ResultSet(multiline query) throws the following exception:
net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.0 user lacks privilege or object not found: E
at net.ucanaccess.jdbc.UcanaccessStatement.executeQuery(UcanaccessStatement.java:210)
at DBConnect.<init>(InOutBoard.java:78)
at InOutBoard.main(InOutBoard.java:47)
Caused by: java.sql.SQLSyntaxErrorException: user lacks privilege or object not found: E
at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source)
at org.hsqldb.jdbc.JDBCStatement.fetchResult(Unknown Source)
at org.hsqldb.jdbc.JDBCStatement.executeQuery(Unknown Source)
at net.ucanaccess.jdbc.UcanaccessStatement.executeQuery(UcanaccessStatement.java:208)
... 2 more
Caused by: org.hsqldb.HsqlException: user lacks privilege or object not found: E
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.QuerySpecification.resolveColumnReferencesForAsterisk(Unknown Source)
at org.hsqldb.QuerySpecification.resolveReferences(Unknown Source)
at org.hsqldb.QueryExpression.resolve(Unknown Source)
at org.hsqldb.ParserDQL.compileCursorSpecification(Unknown Source)
at org.hsqldb.ParserCommand.compilePart(Unknown Source)
at org.hsqldb.ParserCommand.compileStatements(Unknown Source)
at org.hsqldb.Session.executeDirectStatement(Unknown Source)
at org.hsqldb.Session.execute(Unknown Source)
... 5 more
Could not connect to the Database
UCAExc:::4.0.0 user lacks privilege or object not found: E
whereas if I run the second call to ResultSet (single-line query), the data is properly returned from the table. Now as far as I can tell (other than spanning multiple lines in the first one) both queries are the same. So why is it that the first one throws the error? I can't imagine that the choice of formatting would make a difference, but as I said, I'm knew to using ucanaccess.
Any help would be appreciated. Thanks in advance!
The link I regularly give is valid here too:
How to debug dynamic SQL in VBA
You are concatenating a SQL string without checking it is correct.
ResultSet rs = test.executeQuery("SELECT e.* "
+ "FROM Employees e"
+ "WHERE e.EmployeeNo LIKE 'H%'"
+ "ORDER BY e.LastName");
will result in
SELECT e.* FROM Employees eWHERE e.EmployeeNo LIKE 'H%'ORDER BY e.LastName
It's missing spaces between the line breaks.

How to build jpql query which the search condition is multiple attributes of an object?

I have a problem to query database by JPA. Got exception:
org.hibernate.QueryParameterException: Position beyond number of declared ordinal parameters. Remember that ordinal parameters are 1-based! Position: 1
at org.hibernate.engine.query.spi.ParameterMetadata.getOrdinalParameterDescriptor(ParameterMetadata.java:80)
at org.hibernate.engine.query.spi.ParameterMetadata.getOrdinalParameterExpectedType(ParameterMetadata.java:86)
at org.hibernate.internal.AbstractQueryImpl.determineType(AbstractQueryImpl.java:444)
at org.hibernate.internal.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:416)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:440)
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:67)
at com.lawa.service.impl.SubjectServiceImpl.getTotal(SubjectServiceImpl.java:746)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
My code is :
#Transactional(propagation=Propagation.REQUIRED)
public long getTotal(PSubject search) {
StringBuilder jsql = new StringBuilder("select count(*) from PSubject p where p.id=p.id ");
ArrayList paramValues = new ArrayList();
if(search.getName()!=null && search.getName().trim().length()>0) {
jsql.append(" and p.name=:name");
paramValues.add(search.getName().trim());
}
if(search.getType()!=null && search.getType().trim().length()>0) {
jsql.append(" and p.type=:type");
paramValues.add(search.getType().trim());
}
if(search.getMacaddress()!=null && search.getMacaddress().trim().length()>0) {
jsql.append(" and p.macaddress=:macaddress");
paramValues.add(search.getMacaddress().trim());
}
if(search.getUri()!=null && search.getUri().trim().length()>0) {
jsql.append(" and p.uri=:uri");
paramValues.add(search.getUri().trim());
}
if(search.getDescription()!=null && search.getDescription().trim().length()>0) {
jsql.append(" and p.description=:description");
paramValues.add(search.getDescription().trim());
}
if(search.getCreateTime()!=null) {
jsql.append(" and p.createTime=:createTime");
}
Query query = persistService.getEntityManager().createQuery(jsql.toString());
for(int i=0;i<paramValues.size();i++) {
query.setParameter((i+1), paramValues.get(i));
}
if(search.getCreateTime()!=null) {
query.setParameter(":createTime",search.getCreateTime(),TemporalType.DATE);
}
Long count = (Long)query.getSingleResult();
return count;
}
First I want make the code work, and I don't like the code. I feel it's not clean, I have to validate every attribute of "PSubject" one by one and contact the jpql, and I have to pick "createTime" out because its type is "Date". Even more, I add "p.id=p.id" to the jpql,which makes me feel not good.
Please help me to resolve the exception and give a best practice.
If you use named parameters, you should bind them by name:
query.setParameter("macaddress", theMacAddress);
So you should rather use a Map to hold your parameter values, indexed by their name.
But this kind of dynamic query is exactly why the JPA criteria API has been designed. You should use it.
You should use Criteria instead of Query.
It's perfect for this use case.

File download problem in my scala code

I have written the following scala code to download a file . The file gets downloaded correctly but also an exception is thrown. The code is as follows :
var out:OutputStream = null
var in:InputStream = null
try {
var url:URL = null
url = new URL("http://somehost.com/file.doc")
val uc = url.openConnection()
val connection = uc.asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
val buffer:Array[Byte] = new Array[Byte](1024)
var numRead:Int = 0
in = connection.getInputStream()
var localFileName="test.doc"
out = new BufferedOutputStream(new FileOutputStream(localFileName))
while ((numRead = in.read(buffer)) != -1) {
out.write(buffer,0,numRead);
}
}
catch {
case e:Exception => println(e.printStackTrace())
}
out.close()
in.close()
The file gets downloaded but the following exception is thrown :
java.lang.IndexOutOfBoundsException
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:260)
at java.io.BufferedOutputStream.write(BufferedOutputStream.java:105)
at TestDownload$.main(TestDownload.scala:34)
at TestDownload.main(TestDownload.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)
()
Why could this be happening and any way to fix it ?
Please Help
Thank You
Scala returns type Unit, not the type of the value being assigned, with an assignment statement. So
numRead = in.read(buffer)
never returns -1; it doesn't even return an integer. You can write
while( { numRead = in.read(buffer); numRead != -1 } ) out.write(buffer, 0, numRead)
or you can go for a more functional style with
Iterator.continually(in.read(buffer)).takeWhile(_ != -1).foreach(n => out.write(buffer,0,n))
Personally, I prefer the former since it's shorter (and relies less on iterator evaluation happening the way "it ought to").
An alternative option is to use the system commands which is much cleaner and faster from what I can tell.
import sys.process._
import java.net.URL
import java.io.File
new URL("http://somehost.com/file.doc") #> new File("test.doc") !!