TL:DR;
What's the proper syntax/workaround/hack for CF ORM components which have foreign key constraints?
ColdFusion + Integers
Storing a variable as an integer should be an easy programming question, right? Find the appropriate function in the language's documentation (if you can't recall it off the dome) which will say returns: int (owtte) and use that.
Not with ColdFusion.
Some of you may already be aware of ColdFusion's various issues with integers but the one which is currently causing my rapid hair-loss is that ColdFusion will sometimes store your "integer" as either a string, double or long. Now, for most use cases this is not a functionality-breaking issue and is often overlooked/ignored/undiscovered. The exception, in my case, is when ORM comes into play. Of course, as any sensible developer would, I am recognising the possibility of this being down to user error somewhere.
The Problem
The problem occurs whenever CF attempts to interact with the DB. If there is a field which is a foreign key and type 'integer', CF throws a Hibernate Exception with the following message:
java.lang.Integer
That's it. Helpful, right? I found out that this "error message" is explicitly the variable type you're passing into that field, so if you pass a double into the field erroneously, the error message would read java.lang.Double.
Now, this seems to be less about the field itself and more to do with related components - this only happens on a field which has a foreign key constraint linking it to an integer field somewhere else.
Basic example of the kind of thing I'm going for:
Template.cfc
component persistent="true" table="template"{
property name="id" type="numeric" sqltype="integer" column="templateID" fieldtype="id" generator="identity";
property name="userID" type="numeric" sqltype="integer" fkcolumn="userID" fieldtype="many-to-one" cfc="User";
property name="lastModified" type="date" sqltype="timestamp" column="lastModified";
}
User.cfc
component persistent="true" table="user"{
property name="id" type="numeric" sqltype="integer" column="userID" fieldtype="id" generator="identity";
property name="username" type="string" sqltype="nvarchar" column="username";
property name="password" type="string" sqltype="nvarchar" column="password";
}
The problem occurs when flushing the ORM having updated template.userID.
Things I've tried
Contacting Adobe CF Support (we have an enterprise licence with platinum support, but in a month they haven't been able to give me a solution or even any information beyond "it's not working?")
Adding the ormtype attribute to identity fields (and then foreign key fields when that didn't work)
Changing the sqltype property (which I now believe is only for table creation which we never needed anyway since the database was already in place)
Converting the variable using various combinations of the following functions:
NumberFormat( var ) - documentation says it returns 'A formatted number value', actually returns a java.lang.String
LSParseNumber( var )- returns a double
Int( var ) - This one's great: documentation says it returns an integer, as a string. Actually returns a java.lang.Double
Val( var ) - returns a double
javaCast( 'int', var ) - returns an integer, which throws the same error
Setting the elementtype attribute on the property (both ends)
Relaxing the validate attribute on the property (both ends) to 'numeric'
I suspect this may be to do with a combination of Hibernate and CF, rather than just CF, and dispite it's javascript-reminiscent quirks, I still love it.
Environment
Windows Server 2012 R2
ColdFusion 2016 (Enterprise)
SQL Server
TL:DR;
Pass the entity, not the ID.
A foreign key property in CF ORM referencing persistent entity Foo is of type Foo and not int.
More detail
After all this time, Adobe passed me around a few people until I landed on an engineer who knew what he was talking about. It turns out the problem is not so much with the ORM engine (Hibernate) not recognising the integer as an integer, but if you have a foreign key which references another persistent entity, then you must pass the entity and not the ID.
Example (in context of the question)
First, I've removed the type and sqltype values from the question, since the former can be retrieved by CF inspecting the DB (which in my case is fine, but you may have to explicitly set it, especially if you've turned off useDBforMapping in Application.cfc).
Second, I've renamed the userID property in the Template component to user, as it may help those that don't immediately understand what I said above about 'passing the entity' to visualise it better.
Template.cfc
component persistent="true" table="template"{
property name="id" column="templateID" fieldtype="id" generator="identity";
property name="user" fkcolumn="userID" fieldtype="many-to-one" cfc="User";
property name="lastModified" column="lastModified";
}
User.cfc
component persistent="true" table="user"{
property name="id" column="userID" fieldtype="id" generator="identity";
property name="username" column="username";
property name="password" column="password";
}
So, lets say you know the ID of the user who is associated with this template (maybe they're the creator, for example). What you have to do is, not pass the ID to the user property, but rather create a User component, then pass that.
index.cfm
userID = 4; // Example user ID, which we got from somewhere earlier in the code
templateID = 20; // Example template ID, which we got from somewhere earlier in the code
// Create template entity (loading by primary key)
template = EntityLoadByPK( 'Template', templateID );
// To set the user in the Template entity, we must pass a User entity
user = EntityLoadByPK( 'User', userID );
// Only then can we successfully update the template's foreign key property
template.setUser( user );
EntitySave( template );
// Line below is not usually needed, but it forces DB interaction so it's
// useful in the context of this example
ORMFlush();
Hope this helps anyone else who happens across the same confusion.
JC
Related
We are programming a sapjco client to automate certain things. However, one function seems to have problems initializing the functions. Other function blocks can connect correctly, but this one crashes when attempting to connect.
The function doesn't have an import. But we also tried it with import with the same result. So adding one doesn't seem to work as well
FUNCTION ZE237_GET_REZEPTID.
TYPES: BEGIN OF ZE237SOLLDATEN_TYP,
id TYPE ZE237SOLLDATEN-ID,
name TYPE ZE237SOLLDATEN-NAME,
END OF ZE237SOLLDATEN_TYP.
DATA lt_table TYPE TABLE OF ZE237SOLLDATEN.
SELECT
id name
FROM ZE237SOLLDATEN
INTO CORRESPONDING FIELDS OF TABLE T_EXPORTNAME.
ENDFUNCTION.
public IntellifarmSapFunction(JCoDestination destination,
JCoRepository repository, String functionname) throws JCoException, IllegalArgumentException {
this.destination=destination;
this.function = repository.getFunction(functionname); //crash here
}
Expected:
A pointer of the function
Result:
'Unknown type -1 when trying to add field ID to record'
If any of the fields is of ABAP data type int8 then you need at least the JCo 3.0.18 patch level which adds support for this new data type.
You can download the latest JCo patch level from https://support.sap.com/jco
However, I do not recommend to use an ABAP integer type for an ID field. In ABAP integer types are usually used for calculating purposes only. The preferred data type for numerical data values like an ID should be a NUMC (type n) instead.
When I try to retrieve a property from my DocumentModel, using the getProperty(String schemaName, String name) method, I get the property as an Integer Java object instead of a Long Java Object, however Nuxeo is supposed to provide a Long object as shown here.
The field I'm having trouble to get is actually defined as an auto-incremented idfield in the directory.
This used to work just fine in 5.8 but after the upgrade it's not working anymore, what could be the reason of this disfunction ?
EDIT
In my schema the field is defined as the following :
<xs:element name="myid" type="xs:integer" />
In my database it's defined as :
"myid" NUMBER (19) ;
And I have a constraint on the id
ADD CONSTRAINT MYID_PK PRIMARY KEY ( "myid" )
When I try the following
documentModel.getProperty("myTableSchema", "myid");
The return value is correct but the type is an Integer!
Background:
I have a XPage with several controls that are all bound to viewScope variables. Upon a button push event, I run call a java method that will update/insert into DB2 using JDBC (no Hibernate).
In my java method, I have a variable resolver which gets a handle directly to viewScope to access the data. In the viewScope Map, all the data is stored as an Object, and then you have to cast to use the methods of the java.sql.PreparedStatement class. I use this because parameters are added to the prepared statement instead of the SQL. The SQL has question marks (?) where the dependency is added. (This is done for security - company policy).
Here is an simple example:
ps = c.prepareStatement(sql);
ps.setString(1, (String) viewScope.get("firstName"));
The prepared statement has specific methods for each data type. If the data type is wrong you get an exception.
The Problem:
On my XPage I have two numeric edit boxes. They are identical, both use a numeric converter set to currency. Both are set to integer only. When I try to update one is stored as a Long, and one as an Integer.
Here is how I determined that:
System.out.println("NSA object type=" + viewScope.get("manageNSA").getClass().getName());
System.out.println("PNSAC object type=" + viewScope.get("managePNSAC").getClass().getName());
Log file result
02/02/2015 05:45:42 PM HTTP JVM: NSA object type=java.lang.Long
02/02/2015 05:45:42 PM HTTP JVM: PNSAC object type=java.lang.Integer
Where is fails:
ps.setInt(2, (Integer) viewScope.get("manageNSA"));
It also fails on the second one if I have this code.
ps.setLong(6, (Long) viewScope.get("managePNSAC"));
Exception traces:
java.lang.ClassCastException: java.lang.Long incompatible with java.lang.Integer
java.lang.ClassCastException: java.lang.Integer incompatible with java.lang.Long
My Question:
How in XPages can I force the numeric fields to always be a set as longs?? Or how can I force the type to whatever I want, double, long, int, etc ???
What I tried:
Casting both to a Long does not work, which makes no sense. I know that I could have an if statement in my java that first checks the type, and then calls the appropriate method, but I would rather force XPages to set the type the same each time.
You can always create a Long value and set it to a scope variable with
viewScope.managePNSAC = java.lang.Long.valueOf(yourValue);
Make helper class with String get(String) and Long getLong(String) methods with casts inside.
Your code will be:
Helper data = new Helper("viewScope");
ps = c.prepareStatement(sql);
ps.setString(1, data.get("firstName"));
...
ps.setLong(6, data.getLong("managePNSAC"));
Constructor can use strig to identify binded map (can be requestScope or your own bean implementing Map interface).
Try Dojo Number Text Box. It allows you to define the number type it's stored as using javaType.
Try casting to a double. I THINK you need to go through that first. Assuming that works then convert to Long or int from there. I'm not sure... but my gut says you need to go through double when dealing with the xpages front end.
== edit
Another thing you could try is to set the var first...
var myVar:java.lang.Double = document1... whatever
viewScope.put("myVar", myVar)
Something like that maybe...
Use a generic converter tag and specify the converterId you want to use:
<xp:this.converter>
<xp:converter converterId="javax.faces.Integer" />
</xp:this.converter>
<xp:this.converter>
<xp:converter converterId="javax.faces.Long" />
</xp:this.converter>
<xp:this.converter>
<xp:converter converterId="javax.faces.Double" />
</xp:this.converter>
etc...
DDE doesn't do anything to help the discoverability of things that are built-in and come with the JSF environment XPages inherits from.
Edit: I want to change the question a little :-)
I want to save a SaveGame Objet in a SQL database with Hibernate XML. All i want to is to save it. My problem is taht i cant find a way how to do it. I searched now for 3 hours on the internet and didnot find a solution.
All i want to do is to save a integer array as a normal value into my database. I found during my search i can do this with a blob column. But nowhere on this great internet i found the way how to do it. I simply search for a person that can show me how to do it right. Becasue all my tried failed. And i am frustrated now.
My Class that i want to save looks like this:
public class SaveGame implements IDBObject {
int id;
String username;
int basic_game_id;
List<Integer> route = new ArrayList<Integer>();
/*getter + setter */
The SaveGame table should look like this:
ID | username | basic_game_id | route |
1 | Maxi | 1 | xxxxx |
I hope this time its clear what i want to do. I dont have to save every single array entry. I just want to save the whole array in the route column. And i cant find out how i can do this!
Old Question:
I try to map a Integer Array to my database with Hibernate.
This is how my Class looks
public class SaveGame implements IDBObject {
int id;
String username;
int basic_game_id;
List<Integer> route = new ArrayList<Integer>();
/*getter + setter */
My mapping xml looks like this:
<class name="com.travelsales.base_classes.SaveGame" table="SAVEGAME">
<meta attribute="class-description">
This class contains the Save games.
</meta>
<id name="id" type="int" column="id">
<generator class="native"></generator>
</id>
<property name="basic_game_id" type="int" column="basic_game_id"></property>
<property name="username" type="string" column="username"></property>
<list name="route" column="route">
<element type="int"></element>
</list>
</class>
Well i cant find the mistake.
My problem is that i must have an error in the xml mapping part. If i delete the list everything works fine. I want to save integer array or integer list in my database. And i think i have a mistake in the way i do it.
With this code i get the following error:
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.travelsales.controller.MainController.main(MainController.java:42)
Caused by: org.hibernate.InvalidMappingException: Could not parse mapping document from resource Hibernate Config/mapping.hbm.xml
Not sure what you are trying to achieve, do you want to the route to be stored in separate table (to be searchable, etc) or are you happy to have it in same row as your SaveGame.
If your case is really as simple as you presented I would recommend storing in in the same row to save on fetching the int values from the join.
Map the property to a blob column and all is done. I am not using the xml configuration so dont know the syntax but with JPA annotations it is solved by the code:
#Lob
List<Integer> route;
If your hibernate version do not handle interfaces (you will get error messages), change the property declaration form List to ArrayList (which is serializable, so it can be saved as bytes in the blob column).
Well, After all my trys and after all my study from online Documents. Maybe one day another person will have the same problem I had.
Its very simple i was searching for Hibernate types and I found this sentence:
If the type you have is no Basic Hibernate Type and you only want to save it in 1 Column dont write in the Hibernate XML a type and let hibernate decide which typ to use.
If you make your property like normal with name column and type. Hibernate will try if the type is possible for yoru input in my case a int Array. And Hibernate will answer you that the type is unknown. If you set the type to byte[] hibernate will answer you : Do not know to convert int array to byte array.
The Solution is simple just do not set type. Write the property like this:
<property name="route" column="route"/>
Hibernate will now look up which type you will need and set it for you. And everything works fine, and like you want to do it. On the internet you will find alot of resources for array, set and list. All this things will provide a new Tabel special for you array.
If you want to do this go check the internet its full with, helpfull examples. If you just want to save Objects and Hibernate dont know the type just dont set type in your XML.
I have written a program that reads a webservice, retrieving user data, and then is supposed to push that data to ActiveDirectory, thus updating the user's title, address, phone numbers, etc.
The problem is that when I perform the search using the Unboundid Connection class the requested attributes are not returned. Below is the search code:
SearchResult result = connection.search( properties.getProperty("ldap.search.baseDN"),
SearchScope.SUB, "(cn=" + userId + ")",
"personalTitle", "department", "company", "manager", "telephoneNumber",
"streetAddress", "I", "st", "postalCode", "c", "pager", "mobile",
"fax", "cn");
The above code locates the desired user and the cn attribute is returned as expected, but the other attributes all fail to return. If I connect to AD using JXplorer using the same connection credentials, I'm able to see all the desired attributes exist, but are simply not being returned.
I have tried substituting SearchRequest.ALL_OPERATIONAL_ATTRIBUTES, SearchRequest.ALL_USER_ATTRIBUTES and SearchRequest.REQUEST_ATTRS_DEFAULT rather than listing the fields explicitly, but with no success.
I have also looked at the 'Schema' object returned from 'connection.getSchema()' and can see that personalTitle should exist:
connection.getSchema().getAttributeType("personalTitle")
The above code returns:
1.2.840.113556.1.2.615 NAME 'personalTitle' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE
So maybe this is a user permission issue? Has anyone experienced this and know how to resolve it?
Thanks,
Mike
LDAP search result entries only include attributes that actually have values, so the behavior you are seeing from the UnboundID LDAP SDK is appropriate and correct. Even if you explicitly request a particular attribute, that attribute will only be included in the entry if it has one or more values.
I think that you're confused by JXplorer because it's reading the schema to determine what attributes could possibly be included in the entry based on its object classes and is showing them to you so that you can set values for those attributes in the editor. But that doesn't mean that the entry returned by the server actually includes any information about those attributes.
To verify this, you can use the ldap-debugger tool provided with the LDAP SDK to see the actual LDAP communication that occurs. Just run a command like:
tools/ldap-debugger --hostname {directory-server-address} \
--port {directory-server-port} --listenPort {listen-port}
This will create a very simple LDAP proxy server that decodes all requests and responses that pass through it. To use it, simply point JXplorer at the specified listen-port. You will see that when JXplorer retrieves the entry, the entry returned by the server will only contain attributes that actually have values.
If you want to figure out what all the possible attributes are that you can include in a given entry, then use the LDAPConnection.getSchema method to retrieve the server schema, then Schema.getObjectClass for each of the object classes in the target entry, and finally use the ObjectClassDefinition.getRequiredAttributes and ObjectClassDefinition.getOptionalAttributes methods to see what attribute types must and may be used in entries with that object class.