I am new to iBatis. In my project we are using iBatis to persist the java objects in Oracle DB. I have a class, ClassA, which is having 3 columns : id, name and description. The data is going to be persisted in TableA. There is a sequence in DB to generate the value for id column in this table. We wrote the insert statement to this table as follows,
<insert id="insertTableA" parameterClass="com.ClassA">
<selectKey resultClass="java.lang.Long" keyProperty="id">
SELECT seq_TableA.nextval as id FROM dual
</selectKey>
INSERT INTO TableA(ID, NAME, DESCRIPTION) VALUES (#id#, #name#, #description#)
</insert>
This worked fine.
But becaude of our inhouse UI framework limitation we had to change some design. So we need to first generate the id long from sequence, set that value in an instance of ClassA along with name and description and then insert into DB. So in that case the insert statment doesn not need a selectKey attribute. The id, name and description values are in the object. When I updated the query like below, it is throwing Null Pointer Exception.
<insert id="insertTableA" parameterClass="com.ClassA">
INSERT INTO TableA(ID, NAME, DESCRIPTION) VALUES (#id#, #name#, #description#)
</insert>
How we can insert data into table without using a . I am generating the key from the sequence first , populate the object with all values including the id and trying to call the statement from Java as follows,
getSqlTemplate().insert("process.insertTableA", instanceClassA);
Any pointers are welcome,
Thanks,
SD
Just to be sure, did you include a getId() method in your ClassA class so that it will return the value of the id field?
Related
I am using an insert into select mysql query, but in the select statement I am passing a parameter(map) from Java. This query is written using iBatis framework. The query is not recognizing the passed value in the select statement.
I have tried changing #id# to
#{id}, $id$, #{id} and ${id} but didn't succeed.
The query goes like this:
<insert id="someId" parameterClass="map" >
insert into table1(id, column1, column2)
(
select #id#, A.column1, A.column2
from table2 A left outer join table3 B on A.column = B.column
where <condition>
order by column1, column2
)
</insert>
I have sent the request parameter as a 13-digit long id.
In the table1 schema, id has bigint(20) as its datatype.
I want whatever parameter(id) I am passing to the query to be inserted in the table.
Now the problem is that it is not recognizing the value of #id#.
As the id column constraint is not null, it is throwing "MySQLIntegrityConstraintViolationException: Column 'id' cannot be null" after running the above statement.
What should I try instead of #id# to get it working? Or could it be some other problem?
In MyBatis, a #{param} parameter is a "safe" parameter than can only replace scalar values. For safety it cannot be used to add any free content to your SQL statement so you can forget about SQL Injection issues. Even if you try the nastier parameter values you can think of, you'll still be safe and you'll sleep well at night.
Now, if you want to insert any arbitrary content to your SQL (to produce some kind of dynamic SQL) and risk sleepless nights MyBatis offers you ${param} parameters (did you spot the difference?). These string parameters are directly inserted into your SQL statement. With this strategy your query should look like:
<insert id="someId" parameterClass="map" >
insert into table1(id, column1, column2)
(
select ${id}, A.column1, A.column2
from table2 A left outer join table3 B on A.column = B.column
where <condition>
order by column1, column2
)
</insert>
Now, please note this strategy is vulnerable to SQL Injection when not managed appropriately. Make sure the value for the id parameter comes from inside your app and NEVER retrieved from the web page or other external user interface or app.
I can't get the value back from function. It does an insert on a table and must return a number. The data is insert correctly, but the number returned is always null.
Mysql
create table driver_order (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
area_start varchar(200),
area_end varchar(200),
order_number varchar(200),
create_user varchar(200),
primary key (id)
);
DELIMITER $$
CREATE TRIGGER seq_driver_order_number BEFORE INSERT ON driver_order
FOR each ROW
BEGIN
DECLARE seq_type INT(10);
SET seq_type = getUserNo(NEW.create_user);
SET NEW.order_number = getNextCommSequence("motor", seq_type);
END$$
DELIMITER ;
Mybatis
<insert id="insertOrder" useGeneratedKeys="true" keyProperty="id" parameterType="DriverOrder">
INSERT INTO
DRIVER_ORDER(ID,ORDER_NUMBER,AREA_START,AREA_END,CREATE_USER,CREATE_TIME)
VALUES
(#{id},
#{orderNumber,jdbcType=VARCHAR},
#{areaStart,jdbcType=VARCHAR},
#{areaEnd,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR},
now())
</insert>
The return Object all attributes have correctly value include id, except order_number which TRIGGER set value is return null.
Is there something wrong?
The problem is not with a trigger but how to make mybatis get value generated on mysql side during record insertion. Mybatis is rather simple tool in sense that you can't specify properties to columns mapping and everything else happens automagically.
Mybatis core is sql queries not properties-to-columns mappings like in hibernate for example. So mybatis only executes queries and simplifies setting parameters and construction objects from query result.
Nevertheless starting from version 3.2.6 you can use selectKey to get several values and set properties in the object being inserted. If you combine this with last_insert_id() you can get what you need. For your case it is done like this:
<insert id="insertOrder" parameterType="DriverOrder">
<selectKey keyProperty="id,orderNumber" keyColumn="ID,ORDER_NUMBER" order="AFTER" resultType="java.util.Map">
SELECT ID,ORDER_NUMBER FROM DRIVER_ORDER where ID = last_insert_id()
</selectKey>
INSERT INTO
DRIVER_ORDER(ID,ORDER_NUMBER,AREA_START,AREA_END,CREATE_USER,CREATE_TIME)
VALUES
(#{id},
#{orderNumber,jdbcType=VARCHAR},
#{areaStart,jdbcType=VARCHAR},
#{areaEnd,jdbcType=VARCHAR},
#{createUser,jdbcType=VARCHAR},
now())
</insert>
MSSQL(2008) and EclipseLink(2.4)
I'm inserting into a table with a trigger which does an insert into another table
When this happens EclipseLink returns the id of the record inserted into the other table by the trigger.
I assume I can get the correct id by getting EcliipseLink to use SCOPE_IDENTITY() instead of ##IDENTITY.
The question is how to do this?
I need a solution for EclipseLink (JPA), I know how to get the id using sql.
TableA has a trigger that inserts into another table. You issue a command like..
INSERT INTO TableA (Field1, Field2) VALUES (#Value1, #Value2); SELECT SCOPE_IDENTITY() As MyRecID
This will insert a record into TableA and return the ID of that record.
For more info read SCOPE_IDENTITY (Transact-SQL)
There is an example of adding a custom sequence object to EclipseLink to solve this issue here: http://helpdesk.ibs-aachen.de/2009/08/27/eclipselink-ms-sql-server-identity/
I have added two columns in the sql to get the values through hibernate.My databse is oracle and those fields datatype i number. So i have created the beans with long and (tried Integer too) but when retrieving the values(executing the valuesquery).
Its giving me an error
org.hibernate.type.LongType - could not read column value from result set
java.sql.SQLException: Invalid column name
at oracle.jdbc.driver.OracleStatement.getColumnIndex(OracleStatement.java:3711)
at oracle.jdbc.driver.OracleResultSetImpl.findColumn(OracleResultSetImpl.java:2806)
at oracle.jdbc.driver.OracleResultSet.getLong(OracleResultSet.java:444)
at weblogic.jdbc.wrapper.ResultSet_oracle_jdbc_driver_OracleResultSetImpl.getLong(Unknown Source)
at org.hibernate.type.LongType.get(LongType.java:28)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:189)
tABLE DEFINITION :
CREATE TABLE "PRODUCTLIST"
(
PRICELIST_PUBLISH_KEY decimal(22) NOT NULL,
PRODUCT_NBR varchar2(54) NOT NULL,
PRODUCT_KEY decimal(22),
PRODUCT_DESCRIPTION varchar2(360),
PRODUCT_FAMILY_NBR varchar2(30),
PRODUCT_FAMILY_DESCR varchar2(180),
PRODUCT_GROUP_NBR varchar2(30),
PRODUCT_GROUP_DESCR varchar2(180),
PRODUCT_LINE_NBR varchar2(30),
PRODUCT_LINE_DESCR varchar2(180),
PRODUCT_CLASS_CODE varchar2(6),
LAST_PP_GENERATED_DATE_KEY decimal(22),
LAST_PP_GENERATED_DATE date,
PUBLISH_PERIOD_KEY decimal(22) NOT NULL,
PUBLISH_PERIOD_DATE date,
PL_KEY decimal(22),
PRODUCTLIST varchar2(750),
SALES_KEY decimal(22),
PRODUCT varchar2(60),
DM_EXTRACTED_BY_USER varchar2(90)
)
sql :
Query query = session.createSQLQuery(channelQuery)
.addScalar("PRODUCT",Hibernate.STRING)
.addScalar("PRODUCTLIST",Hibernate.STRING)
.addScalar("PRODUCTKEY",Hibernate.LONG)
.addScalar("SALESKEY",Hibernate.LONG)
.setResultTransformer(Transformers.aliasToBean(SearchResult.class));
return query.list();
}
});
Please help me to fix the issue ?
In your table definition, I can't see all the fields you're using in the addScalar() methods: there are no PRODUCTKEY nor SALESKEY fields. Instead I can see a PRODUCT_KEY and a SALES_KEY fields (underscores). I think you should use the correct name of the fields in the addScalar() methods.
But if your query is the one you put in your comments, you have to correct some details:
you should use p instead of pub as alias for the table name. As there is only one table in the query, you can suppress the alias.
In your SELECT clause, p.productprice is not an existing field in your table. Maybe you want to use p.pricelist instead.
In your WHERE clause, p.productnbr is not an existing field in your table. You should use p.product_nbr instead.
Then you should change the field names in the addScalar() methods to match those you are using in the query.
Modified query
SELECT distinct p.product, p.productlist, p.PL_KEY, p.SALES_KEY
FROM productlist p
WHERE p.product_nbr in ('1002102')
Your code should be:
Query query = session.createSQLQuery(channelQuery)
.addScalar("PRODUCT",Hibernate.STRING)
.addScalar("PRODUCTLIST",Hibernate.STRING)
.addScalar("PL_KEY",Hibernate.LONG)
.addScalar("SALES_KEY",Hibernate.LONG)
.setResultTransformer(Transformers.aliasToBean(SearchResult.class));
return query.list();
If you define aliases in your query, then you can use the alias names instead of the field names. For example, with this query:
SELECT distinct p.product, p.productlist, p.PL_KEY as PRODUCTKEY, p.SALES_KEY as SALESKEY
FROM productlist p
WHERE p.product_nbr in ('1002102')
you can use the following code (it's your original code):
Query query = session.createSQLQuery(channelQuery)
.addScalar("PRODUCT",Hibernate.STRING)
.addScalar("PRODUCTLIST",Hibernate.STRING)
.addScalar("PRODUCTKEY",Hibernate.LONG)
.addScalar("SALESKEY",Hibernate.LONG)
.setResultTransformer(Transformers.aliasToBean(SearchResult.class));
return query.list();
I'm using iBatis/Java and Postgres 8.3.
When I do an insert in ibatis i need the id returned.
I use the following table for describing my question:
CREATE TABLE sometable ( id serial NOT NULL, somefield VARCHAR(10) );
The Sequence sometable_id_seq gets autogenerated by running the create statement.
At the moment i use the following sql map:
<insert id="insertValue" parameterClass="string" >
INSERT INTO sometable ( somefield ) VALUES ( #value# );
<selectKey keyProperty="id" resultClass="int">
SELECT last_value AS id FROM sometable_id_seq
</selectKey>
</insert>
It seems this is the ibatis way of retrieving the newly inserted id. Ibatis first runs a INSERT statement and afterwards it asks the sequence for the last id.
I have doubts that this will work with many concurrent inserts. ( discussed in this question )
I'd like to use the following statement with ibatis:
INSERT INTO sometable ( somefield ) VALUES ( #value# ) RETURNING id;
But when i try to use it within a <insert> sqlMap ibatis does not return the id. It seems to need the <selectKey> tag.
So here comes the question:
How can i use the above statement with ibatis?
The <selectKey> element is a child of the <insert> element and its content is executed before the main INSERT statement. You can use two approaches.
Fetch the key after you have inserted the record
This approach works depending on your driver. Threading can be a problem with this.
Fetching the key before inserting the record
This approach avoids threading problems but is more work. Example:
<insert id="insert">
<selectKey keyProperty="myId"
resultClass="int">
SELECT nextVal('my_id_seq')
</selectKey>
INSERT INTO my
(myId, foo, bar)
VALUES
(#myId#, #foo#, #bar#)
</insert>
On the Java side you can then do
Integer insertedId = (Integer) sqlMap.insert("insert", params)
This should give you the key selected from the my_id_seq sequence.
Here is simple example:
<statement id="addObject"
parameterClass="test.Object"
resultClass="int">
INSERT INTO objects(expression, meta, title,
usersid)
VALUES (#expression#, #meta#, #title#, #usersId#)
RETURNING id
</statement>
And in Java code:
Integer id = (Integer) executor.queryForObject("addObject", object);
object.setId(id);
This way more better than use :
It's simpler;
It have not requested to know sequence name (what usually hidden from postgresql developers).