Java- PLSQL- Call Table of records from java - java

PLSQL package-procedure declarations
TYPE custom_type IS TABLE OF single_rec_type;
TYPE single_rec_type IS RECORD(
//id, name etc
)
Problem:
But custom_type has no direct Java type representation [like OracleTypes.CLOB or OracleTypes.CURSOR]
because custom_type is a PLSQL type and not a SQL type.
When I googled, I came across these two options:
To represent it ,
(1) create a SQL TYPE from procedure(or a wrapper PLSQL function) that we can bind from java.
Reference: java - passing array in oracle stored procedure
(2) Register the output parameter with our type and use SQLData object to represent a record.
Reference: Howto get a table as a out parameter in oracle
callableStatement.registerOutParameter(8, OracleTypes.ARRAY, "custom_type");
On doing this, I get the error:
java.sql.SQLException: invalid name pattern: MYDB_OWNER.custom_type
at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:554)
at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:471)
One suggestion was to declare the custom_type TYPE inside the schema, instead of declaring inside the package.
or by creating public synonym and giving grants.
Question - Regarding the second approach, is it correct practice to declare any custom type in schema level?

Yes, That's the only way it works. I followed the link mentioned in second approach
Howto get a table as a out parameter in oracle
and it worked. The Package level changes included (1) Declaring the custom_type and single_rec_type in schema level [as global, not inside the package] and (2) Replacing IS RECORD with AS OBJECT.
The Java code changes apart from what was mentioned in the link, includes giving the complete name for the class in map.put("SINGLE_REC_TYPE", Class.forName("com.example.SRecord"));
Another thing to notice is that in that example, it mentioned stream.readString();. If you read the API, it says 'Reads the next attribute in the stream and returns it as a String in the Java programming language.' . So if you have three attributes inside the object, then use the method three times like this
id = stream.readString();
name = stream.readString();
designation = stream.readString();
Another point is well mentioned in that post; Regarding the datatypes of attributes inside the object. If there are type mismatch, you get internal representation errors.
eg: correct way:
SRecord.java
public String id; \\varchar in plsql procedure
public String name; \\varchar in plsql procedure

Related

Spring data Exists By Name and Different value for other field

There is a way in Spring Data to find if exists a Name that not contains a Code?
Something like:
existsNameWithDifferentCode(String name, Long code);
I'm doing a update and I have to check if this update doesn't contain the same Name as another.
If I cannot do this with the Spring Data signature method, there is a way to do that with ExampleMatcher?
Or any suggestion?
You could use this method name
existsByNameAndCodeNot(String name, Long code);
I recommend to write a JPQL request.
This approach will allow you to more accurately obtain the necessary data from the database without:
thatLongNameForShortRequestWithMultipleConditions()

Is there a way to specify collection name for Java bean being stored into RavenDB?

I'm using RavenDB with JAVA ravendb-jvm-client for my app, where existing collection names does'n reflect 'User.java' -> 'Users' convention. Is there a way how to specifiy correct collection name for java beans so java client use this instead of automatic convention? Something similar to #Table annotation for JPA.
I know I can specify collection name for example in queries e.g. session.query(User.class, Query.collection("custUsers")); ... but its very verbose to specify it repeatedly.
You need to use FindCollectionName convention of the DocumentStore.
In Java, you can use setFindCollectionName of the conventions.
From https://ravendb.net/docs/article-page/4.1/java/client-api/session/configuration/how-to-customize-collection-assignment-for-entities :
By default a collection name is pluralized form of a name of an entity type. For example objects of type Category will belong to Categories collection. However if your intention is to classify them as ProductGroups use the following code:
store.getConventions().setFindCollectionName(clazz -> {
if (Category.class.isAssignableFrom(clazz)) {
return "ProductGroups";
}
return DocumentConventions.defaultGetCollectionName(clazz);
});
Documentation on conventions in general: https://ravendb.net/docs/article-page/4.1/java/client-api/configuration/identifier-generation/global

Dynamically show fields of the generic type through intellisense

In Servoy, a development and deployment platform, you have the possibility to use what is called a JSFoundSet which is an object containing record objects defined by its SQL. Such a JSFoundSet is created as follows using the appropriate annotation:
/** #type{JSFoundSet<db:/database/table_name}*/
var fs = null;
from this point on you can use in your code the variable fs to get or set values to the properties of table_name. So if I would create a foundset of table Customer and this table contains the columns id, firstName and lastName, then the Servoy platform provides intellisense that allows me to do this:
fs.id = 1;
fs.firstName = 'John';
fs.lastName = 'Doe';
Since I use a lot of Java too, I wanted to see if I can create something similar of this in Java. So I want to create a class FoundSet of a certain generic type, say of type Customer in our example, after which in my code I can create an object of this class and then access the public fields (set/get) of FoundSet. While typing I wish to see these fields show up through intellisense.
Is there a library or some sort that allows me to define some annotations as the Servoy example to accomplish this?

jOOQ : compare uuid(from postgresql) and string(user id in class)

My backend is postgresql. I am trying to write a simple function to fetch data from database, using jooq records. The DAO for this table is written by me. The problem I am facing is comparison of UUID in db and String provided to function.
public Correspondence fetchByExternalId(String externalId) {
CorrespondenceRecord correspondenceRecord =
create.fetchOne(Tables.CORRESPONDENCE,
Tables.CORRESPONDENCE.USERID.eq(externalId));
CORRESPONDENCE.USERID is UUID and externalId is String; The operator eq is the one which i am not able to implement here, which has worked for me on previous occasions.
The error shown is :
Cannot resolve method 'eq(java.lang.String)'
when i referred to the jOOQ site for help it show 'eq' is the operator it suggests to be used!
Can anyone help me with an alternative or tell me where i went wrong?
jOOQ is a very type safe API, so you cannot compare UUID types with String types using eq(), because the eq() method uses the generic <T> type of your CORRESPONDENCE.USERID column, which is UUID:
TableField<..., java.util.UUID> USERID = ...
Generating a UUID bind value in the Java client
You either have to supply an externalId in the form of a UUID type, e.g.:
CORRESPONDENCE.USERID.eq(UUID.fromString(externalId))
... or, you let jOOQ convert that type for you:
CORRESPONDENCE.USERID.eq(CORRESPONDENCE.USERID.getDataType().convert(externalId))
Both of the above are equivalent.
Generating a UUID bind value in the database
You can always also defer the type conversion work to the database by casting (which will result in a CAST(? AS UUID) being rendered:
CORRESPONDENCE.USERID.eq(DSL.cast(externalId, UUID.class));
... or by coercing the variable to adhere to the UUID type (trusting that the database can implicitly convert the VARCHAR type to a UUID type):
CORRESPONDENCE.USERID.eq(DSL.coerce(DSL.val(externalId), UUID.class));

How to return ids on Inserts with mybatis in mysql with annotations

See this related question for Postgres. For some reason, the solution doesn't work for me - the return value of the insert statement is always "1".
See this other question for an XML based solution. I would like to do the same without XML - insert a record and find the new auto-generated id of the record I just insreted.
I didn't find a matching annotation to <selectkey> (see this open issue)
How do I proceed?
Examining mybatis code reveals that INSERT is implemented via UPDATE, and always returns the number of inserted rows! So ... unless I'm completely missing something here, there's no way to do this using the current (3.0.3) implementation.
Actually, it's possible to do it, with the #Options annotation (provided you're using auto_increment or something similar in your database) :
#Insert("insert into table3 (id, name) values(null, #{name})")
#Options(useGeneratedKeys=true, keyProperty="idName")
int insertTable3(SomeBean myBean);
Note that the keyProperty="idName" part is not necessary if the key property in SomeBean is named "id". There's also a keyColumn attribute available, for the rare cases when MyBatis can't find the primary key column by himself. Please also note that by using #Options, you're submitting your method to some default parameters ; it's important to consult the doc (linked below -- page 60 in the current version) !
(Old answer) The (quite recent) #SelectKey annotation can be used for more complex key retrieval (sequences, identity() function...). Here's what the MyBatis 3 User Guide (pdf) offers as examples :
This example shows using the #SelectKey annotation to retrieve a value from a sequence before an
insert:
#Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
#SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
This example shows using the #SelectKey annotation to retrieve an identity value after an insert:
#Insert("insert into table2 (name) values(#{name})")
#SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
The <insert>, <update>and <delete> statements return the number of affected rows, as is common with database APIs.
If a new ID is generated for the inserted row, it is reflected in the object you passed as a parameter. So for example, if you call mapper.insert(someObject) inside your annotated insert method, after inserting, you can call someObject.getId (or similar) to retrieve it.
Using the options of <insert>, you can tweak how (by providing an SQL statement) and when (before or after the actual insertion) the id is generated or retrieved, and where in the object it is put.
It may be instructive to use the MyBatis generator to generate classes from a database schema and have a look at how inserts and updates are handled. Specifically, the generator produces "example" classes that are used as temporary containers to pass around data.
you can get your generated ids from save methods,
lets say a bean with ID and name properties,
bean.setName("xxx");
mapper.save(bean);
// here is your id
logger.debug(bean.getID);
I didn't like most of the answers I found online for returning generated keys because
All of the solutions I found called a "setter" on the inbound object
None of the solutions returned the generated column from the method
I came up with the following solution which addresses points 1 & 2 above which
Passes two parameters to mybatis "in" & "out" (mybatis does not mutate "in", it calls a setter on "out")
Requires an additional default method on the interface to return the value
public interface MyMapper {
/**
* this method is used by the mybatis mapper
* I don't call this method directly in my application code
*/
#Insert("INSERT INTO MY_TABLE (FOO) VALUES ({#in.foo})")
#Options(useGeneratedKeys=true, keyColumn="ID", keyProperty = "out.value")
void insert(#Param("in") MyTable in, #Param("out") LongReference out);
/**
* this "default method" is called in my application code and returns the generated id.
*/
default long insert(MyTable tableBean) {
LongReference idReference = new LongReference();
insert(tableBean, idReference);
return idReference.getValue();
}
}
This requires an additional class which can be re-used on similar methods in future
public class LongReference {
private Long value;
// getter & setter
}

Categories

Resources