JDBC querying and inserting into arbitrary tables - sql

What is a good way to query (always selecting all columns) and insert into arbitrary Oracle database tables using JDBC? I created the following method (haven't tested it yet) for retrieving any table's attribute names and types (I need the names for display purposes):
public ArrayList<Map<String, String>> retrieveTableAttributes(String tableName) throws SQLException{
ArrayList<Map<String, String>> attributes = new ArrayList<>();
DatabaseMetaData dbmd = conn.getMetaData();
ResultSet resultSet = dbmd.getColumns(null, null, tableName, null);
int i = 0;
while (resultSet.next()) {
attributes.add(new HashMap<String, String>());
attributes.get(i).put(resultSet.getString("COLUMN_NAME"), resultSet.getString("TYPE_NAME"));
i++;
}
resultSet.close();
return attributes;
}
I found this method PreparedStatement#setObject(int parameterIndex, Object x, int targetSqlType) which I think can be used to set a value for any arbitrary column type (in this case I would need to get the column type as an int instead of the type name), but I am not sure about this. So, is there a better way for setting and more importantly getting values from columns with arbitrary types? If it helps, I am trying to create a Java EE GUI tool for manipulating a database (adding, deleting, updating rows from any table in the database).

A simple method to get the type code from a named type using reflection:
public int getTypeByName(String name) {
for (Field field : java.sql.Types.class.getFields()) {
if (name.equalsIgnoreCase(field.getName())) return (Integer) field.get(null);
}
return -1;
}
Note that the TYPE_NAME column is probably not guaranteed to match Oracle's column support. A safer bet might just be to rely on the driver to make the right conversion choices using the untyped setObject and getObject methods.

Related

Verify HTML Table using Selenium

Can someone let me know which approach is good for verifying HTML Table. I have to write test case related to adding/deleting/editing HTML table using selenium.
Table looks like this
Name and Branch could be duplicate, the only way to uniquely Identify the row is using id (First column).
I am using the page object pattern, so I need to write the function for adding/editing/deleting and verifying that row exist or not.
how should I write a function to add record and then a separate function to verify that record has been added successfully. As the name could be duplicate I can not verify if name exist in the table or not. The other way is, I return the ID of last row once I add the record. And then I can verify if name on last id is same as I recently added. However this approach has some glitch as well.
is there any better approach for this situation.
For add test case, you could create a function in your PageObject that returns the count of rows that have the same value in the Name field.
In your test case,
Store this number in a variable
Add a record
Back to the grid screen, invoke the row counting function again, comparing it with the stored value
It must be the stored value + 1, otherwise something is wrong.
As an additional check, you could also store the total number of rows in the table prior adding, and check if that value has also increased by one after edition (ensuring no record has been replaced in the process).
Note: If you're using PageFactory, you could also create an IList with the rows in the table, and use its count property instead of creating a counting function.
Edit:Your add method can be stored inside your PageObject, so that you have it separated from assertion. Even if you return the id, without knowing what was in the table prior adding your record, a separated individual verification won't be of much use.
So without seeing the html for this table, I'll have to make some assumptions. It looks like the ID column are hyperlinks, so the xpath for those will be slightly different from the other cells. Okay, so onto the solution.
We first need to think about how we're going to store the data from the table. You want some kind of 2 dimensional object that is easy to work with. I like to use HashMap to store the individual rows. HashMap stores information in un-ordered, key-value pairs. They Key would be the name of the column, and the Value would be the value of that cell for that row.
To store a table's worth of data, I would then put each of those rows that are stored in separate HashMaps and put that into an ArrayList.
Now onto how to scrape the data, the fastest solution is always the one that does the least number of lookups and calls to Selenium. In this case since you have all kinds of weirdness going on with the table, I would target all of the data cells with one lookup.
Without seeing the HTML, my xpath would look something like //table[#id='something']/tbody/tr/td[1]/a|//table[#id='something']/tbody/tr/td[not(./a[./text()='View' or ./text()='Edit' or ./text()='Delete'])]
The Pipe symbol conjoins the 2 xpaths together so it will target both types of cells in the correct order.
Here's my generic TableData class. You create an instance of this class to store the data. The data itself gets stored in the _data object.
public class TableData {
public ArrayList<HashMap<String, String>> _data;
public TableData(ArrayList<HashMap<String, String>> data) {
_data = data;
}
public static TableData get_SiteTable_ByCell(WebDriver driver, By tableCells, String[] headerValues) {
ArrayList<WebElement> tableCellElements = new ArrayList<WebElement>(driver.findElements(tableCells));
ArrayList<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
int numberRows = tableCellElements.size() / headerValues.length;
for (int i = 0; i < numberRows; ) {
HashMap<String, String> newRow = new HashMap<>();
for (int j = 0; j < headerValues.length; j++, i++) {
newRow.put(headerValues[j], tableCellElements.get(i).getText());
}
data.add(newRow);
}
return new TableData(data);
}
}
So now that we have our method for getting the data, the values that I need to pass in are...
public TableData getTable(WebDriver driver)
{
String[] headers = {"ID", "Name", "Branch"};
By cellsBy = By.xpath("//table[#id='something']/tbody/tr/td[1]/a|//table[#id='something']/tbody/tr/td[not(./a[./text()='View' or ./text()='Edit' or ./text()='Delete'])]");
return TableData.get_SiteTable_ByCell(driver, cellsBy, headers);
}
Okay, so now we want to get a specific row. I need to write a method that will return a HashMap for the given criteria. This will also go in the TableData class from earlier.
public HashMap<String, String> returnRow(String columnName, String columnValue){
for(HashMap<String, String> row : _data){
if(row.get(columnName).equals(columnValue)){
return row;
}
}
return null;
}
So now that I have all that, my code would look something like...
#Test
public void newTest1()
{
TableData customerTable = getTable(driver);
HashMap<String, String> rowWeWant = customerTable.returnRow("ID", "1");
String valueWeWant = rowWeWant.get("Name");
}

Hibernate SQL transformation fails for Enum field type

I am using a SQL query and then transforming the result using Hibernates's Transformers.aliasToBean().
One of the columns in my query is an enum. The transformation somehow fails for the enum. What should I do? Which datatype should I use? I want more than 1 character to transform the result into my enum type.
This is how the simplified version of my query/code looks like (b is an enum in the table profiles):
session.createSQLQuery("select a, b from profiles").setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
Exception : expected type: Foo.ProfileStateEnum, actual value: java.lang.Character
Assuming that the java enum type that corresponds to column b is Foo.ProfileStateEnum, the following code snippet should work for you. (I tested with Hibernate 4.1.6)
import java.util.Properties;
import org.hibernate.type.Type;
import org.hibernate.type.IntegerType;
import org.hibernate.internal.TypeLocatorImpl.TypeLocatorImpl;
import org.hibernate.type.TypeResolver.TypeResolver;
import org.hibernate.type.EnumType;
Properties params = new Properties();
params.put("enumClass", "Foo.ProfileStateEnum");
params.put("type", "12"); /*type 12 instructs to use the String representation of enum value*/
/*If you are using Hibernate 5.x then try:
params.put("useNamed", true);*/
Type myEnumType = new TypeLocatorImpl(new TypeResolver()).custom(EnumType.class, params);
List<Profile> profileList= getSession().createSQLQuery("select a as ID, b from profiles")
.addScalar("ID", IntegerType.INSTANCE)
.addScalar("b", myEnumType )
.setResultTransformer(Transformers.aliasToBean(Profile.class))
.list();
I found two ways to achieve it.
Use org.hibernate.type.CustomType with org.hibernate.type.EnumType(put either EnumType.NAMED or EnumType.TYPE, see EnumType#interpretParameters). Like below:
Properties parameters = new Properties();
parameters.put(EnumType.ENUM, MyEnum.class.getName());
// boolean or string type of true/false; declare database type
parameters.put(EnumType.NAMED, true);
// string only; declare database type
parameters.put(EnumType.TYPE, String.valueOf(Types.VARCHAR));
EnumType<MyEnum> enumType = new EnumType<>();
enumType.setTypeConfiguration(new TypeConfiguration());
enumType.setParameterValues(parameters);
CustomType customEnumType = new CustomType(enumType);
Another simple way. Use org.hibernate.type.StandardBasicTypeTemplate with org.hibernate.type.descriptor.sql.*TypeDescriptor. Like below:
StandardBasicTypeTemplate<MyEnum> enumType =
new StandardBasicTypeTemplate<>(VarcharTypeDescriptor.INSTANCE,
new EnumJavaTypeDescriptor<>(MyEnum.class));
Let's see why you are getting this exception.
From the question it is obvious that you have used #Enumerated(EnumType.STRING) annotation for the field 'b' in you model class. So the field is an enum for your model class and a varchar for your database. Native SQL is not concerned about you model class and returns what ever is there in the database table as it is. So in your case, the SQLQuery you are using will return a String for 'b' instead of a ProfileStateEnum type. But your setter method for 'b' in the Profile class takes a ProfileStateEnum type argument.
Thus you get the exception "expected type: Foo.ProfileStateEnum, actual value: java.lang.Character"
You can use Aliasing to solve the problem.
What I suggest is, alias your column with any name you want and create a setter method for that alias in your model/dto.
For example, lets alias your column as 'enumStr'.
Then your query will look like this : "select a, b as enumStr from profiles"
Now, create a setter method for that alias in the your Profile class.
(Assuming that the enum ProfileStateEnum can have any of the two values STATE1 and STATE2)
public void setEnumStr(String str){
/*Convert the string to enum and set the field 'b'*/
if(str.equals(ProfileStateEnum.STATE1.toString())){
b = ProfileStateEnum.STATE1;
} else {
b = ProfileStateEnum.STATE2;
}
}
Now on transforming, the setter for the alias setEnumStr(String) will be invoked instead of setter for the field setB(ProfileStateEnum) and the string will be converted and saved to the type you want without any exceptions.
I am a beginner in Hibernate and the solution worked for me. I am using PostgreSQL. But I believe it works for other databases too.

Restricting an NHibernate query using ICriteria according to an enumeration of enums

I have an entity, with a field of type enum, that is persisted as an integer in my database.
When retrieving objects from the database using ICriteria, I wish to restrict the results to those with the field being a member of a collection of enum values. Does Restrictions.In work with a collection of enums?
The following does not work. Do I have to perform something like type-casting at the "restrictions.in" part of the query?
var myEnumCollection = new MyEnum[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
FindAll is a method encapsulating
criteria.GetExecutableCriteria(Session).List<MyType>()
My initial guess would be that you'll need to compare against the integer values of the enum members (assuming that you're mapping the enum as an integer); so something like:
var myEnumCollection = new int[] { MyEnum.One };
return FindAll<MyType>(Restrictions.In("EnumProperty", myEnumCollection));
May be the solution that you're after. If you update your post with further details (mapping of the enum member and the sql being generated by the query), I may be able to provide further assistance.

Find Underlying Column Size Via NHibernate Metadata

Is there a way to use SessionFactory.GetClassMetadata(), or any other method you're aware of, to dynamically get the maximum size of a varchar column that underlies an NHibernate class' string property?
To clarify, I'm not looking to read a length attribute that's specified in the NHibernate mapping file. I want to deduce the actual database column length.
See the code below for two different ways you can get the column size for a string from NHib metadata.
Cheers,
Berryl
[Test]
public void StringLength_DefaultIs_50_v1()
{
_metadata = _SessionFactory.GetClassMetadata(typeof(User));
var propertyType = _metadata.GetPropertyType("Email") as StringType;
Assert.That(propertyType.SqlType.Length, Is.EqualTo(50));
}
[Test]
public void StringLength_DefaultIs_50_v2()
{
var mapping = _Cfg.GetClassMapping(typeof(User));
var col = mapping.Table.GetColumn(new Column("Email"));
Assert.That(col.Length, Is.EqualTo(50));
}
When the Session factory is generated the NH engine does not check (and retrieve) what the underlying database is. For your case either you provide a "rich" mapping to have everything available at runtime, OR make a function that reads the necessary information from the DB (ie select * from sys.columns ..... for sql-server) when you need it.
Mind you that a rich mapping also allows the NH engine to make some automations (like checking if the size of the string passed is larger than the length of the (n)varchar column)

How to make a return type for a result set in LINQ

I am having a problem determining how c# and LINQ solve the common problem of handling a data structure that does not necessarily return a table structure, but instead a resultset.
I have a stored procedure that works, and have included it in my DBML
[Function(Name="dbo.p_GetObject")]
public int p_GetObject([Parameter(Name="ObjectType", DbType="NVarChar(200)")] string objectType, [Parameter(Name="ItemState", DbType="Bit")] System.Nullable<bool> itemState, [Parameter(Name="IsPublished", DbType="Bit")] System.Nullable<bool> isPublished)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), objectType, itemState, isPublished);
return ((int)(result.ReturnValue));
}
The dbml says that the return type is (None) and this could be the crux issue. However I don't have a DBML object that matches the resultset.
The SP takes three parameters, and returns a result set with three columns (ID, Name, Value) with multple rows. I can create a data object for this, and call it resultSet
When I write a function call for this, I get stuck:
public List<resultset> GetObject(string objectType, bool itemState, bool isPublished)
{
MyDataContext.p_GetObject(objectType, itemState, isPublished);
}
My questions are:
how do I have the data context call to the stored procedure populate my resultSet object? Is there a better approach? What should the return type be? A SQL view? Looking for good suggestions...
If it simply isn't understanding your SP, that could be the SET FMT_ONLY issue... try generating the data from a simplified version of the SP?
Normally, SPs / UDFs that don't map 1:1 with an existing entity would expose themselves in a generated type. You can rename this in the DBML file (not in the designer), but personally I wouldn't; I tend to mark the SP as private, and write my own method that projects into my own POCO type (defined for the repository):
var typed = from row in cxt.SomeFunction(123)
select new MyType {Id = row.Id, Name = row.Name, ...}
The reason for this is partly for repository purity, and partly to guard against the designer's habit of re-writing the DBML in unexpected ways ;-p See here for more.