MyBatis - How to join multiple columns from same table - spring

I'm trying to learn how to join multiple columns from one table to a single column from another table, for a given Id.
Here is my team table:
create table teams (
id varchar(10),
name varchar(30),
primary key (id)
);
Here is my teammatch table:
create table teammatch (
id integer,
firstTeamId varchar,
secondTeamId varchar,
matchId integer,
primary key(id),
foreign key (firstTeamId) references teams (id),
foreign key (secondTeamId) references teams (id)
);
My sql:
select teammatch.*, t1.*, t2.*
from teammatch
inner join teams t1 on teammatch.firstTeamId = t1.id
inner join teams t2 on teammatch.secondTeamId = t2.id
where teammatch.id = #{id}
Data:
ID FIRSTTEAMID SECONDTEAMID
1 POR DEN
2 TOR PHI
This query returns POR team for both t1 and t2 but I need it to
return POR for t1 and DEN for t2
EDIT:
This works fine when I write sql query in H2 database, but when I write it with mybatis in xml mapper file, It returns same value for both fields.
Here is my ResultMap
<resultMap id="TeamMatchMap" type="TeamMatch">
<id column="id" property="id" />
<association property="firstTeamId" column="firstTeamId" javaType="Team">
<id column="id" property="id" />
<result column="name" property="name" />
</association>
<association property="secondTeamId" column="secondTeamId" javaType="Team">
<id column="id" property="id" />
<result column="name" property="name" />
</association>
</resultMap>

pleasy try to change your mapper to something like this
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
<select id="getTeammatches"
resultMap="teamMatchMap">
select
teammatch.id as matchid ,
t1.id as firstteam_id ,
t1.name as firstteam_name,
t2.id as secondteam_id ,
t2.name as secondteam_name
from teammatch
inner join teams t1 on teammatch.firstTeamId = t1.id
inner join teams t2 on teammatch.secondTeamId = t2.id
where teammatch.id = #{id}
</select>
<resultMap type="TeamMatch" id="teamMatchMap">
<result property="id" column="MATCHID"/>
<association property="team1" columnPrefix="FIRSTTEAM_" resultMap="teamMap"/>
<association property="team2" columnPrefix="SECONDTEAM_" resultMap="teamMap"/>
</resultMap>
<resultMap type="Team" id="teamMap">
<result property="id" column="ID"/>
<result property="name" column="NAME"/>
</resultMap>
</mapper>
by declaring a separate resultMap for a Team or referring to an existing ones, you dont need to map columns twice in the body-tag of association-tag
you used the column-Attribute for association - as far as i know, its only used if you want myBatis to execute anoter select-tag
Edit
i wrote the mapper before i read your edit and comment
if your Team-class provides a contructor that takes id and name, your teamMap can also look like this
<resultMap type="Team" id="teamMap">
<constructor>
<idArg column="ID"/>
<arg column="NAME"/>
</constructor>
</resultMap>

Related

Multiple selection from different tables in one query

My question is about optimizing queries with mybatis.
List<SomeObject> someObjectList = oracleMapper.getAllSomeObject();
in my project I have to unload data from one database and make a request to another
List<String> subscriptionIds = someObjectList.stream().map(g -> g.getSubscriptionId()).collect(Collectors.toList());
List<Subscription> subscriptionsByIds = postgresMapper.getSubscriptionsByIds(subscriptionIds);
List<SubscriptionSubject> subjectsByIds = postgresMapper.getSubscriptionSubjectsByIds(subscriptionIds);
I have to do a query using a list of id to different tables twice
<select id="getSubscriptionsByIds" resultType="com.mappers.Subscription">
SELECT "s_id" as sId, "old" as old FROM "subscriptions"."subscriptions" s where s."subscription_id_old" IN
<foreach item="item" collection="subscriptionIds" separator="," open="(" close=")">
#{item}
</foreach>
</select>
<select id="getSubscriptionSubjectsByIds" resultType="com.mappers.SubscriptionSubject">
SELECT "s_id" as sId, "old" as old FROM "subscriptions"."subscription_subjects" s where s."subscription_id_old" IN
<foreach item="item" collection="subscriptionIds" separator="," open="(" close=")">
#{item}
</foreach>
</select>
#Data
public class Subscription {
private Long subscriptionId;
private String subscriptionIdOld;
//others
}
#Data
public class SubscriptionSubject {
private Long subscriptionId;
private String subscriptionIdOld;
//others
}
is it possible somehow not to do two queries, but get by with one for two tables?
There is a lot of data and there will be a lot of requests too, I would like to reduce it. thanks in advance
If the only difference between both queries is the table name. You can pass that value as an extra parameter and add it to the query using "SQL Injection".
For example:
<select id="getSubscriptionsByIds" resultType="com.mappers.Subscription">
SELECT "s_id" as sId, "old" as old FROM ${tableName} s
WHERE s."subscription_id_old" IN
<foreach item="item" collection="subscriptionIds" separator=","
open="(" close=")">
#{item}
</foreach>
</select>
Make sure the object you are passing as a parameter includes an extra property tableName apart from the list of IDs you are already passing.
Notice that in order to implement SQL Injection you need to use ${parameter} instead of #{parameter}. Use SQL Injection when absolutely necessary only. In the wrong hands it can open security vulnerabilities in your application.

Update Timestamp after jdbc:inbound-channel-adapter reads a record from DB

I am using Spring integration's "int-jdbc:inbound-channel-adapter" to fetch records from the DB. However after I fetch records I also need to update 2 columns
1) Status column
2) Timestamp columns
Updating the status column is not an issue as I can use below xml snippet
<int-jdbc:inbound-channel-adapter query="select * from item where status=2"
channel="target" data-source="dataSource"
update="update item set status=10 where id in (:id)" />
However when I try to update timestamp, it doesnt work
<int-jdbc:inbound-channel-adapter query="select * from item where status=2"
channel="target" data-source="dataSource"
update="update item set status=10,timestamp=:timestamp where id in (:id)"
update-sql-parameter-source-actory="timestampUpdaterSqlParameterSourceFactory">
<int-jdbc:inbound-channel-adapter>
<bean id="timestampUpdaterSqlParameterSourceFactory"
class="org.springframework.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory" >
<property name="parameterExpressions">
<map>
<entry key="timestamp" value="#now"/>
</map>
</property>
</bean>
<bean id="now" scope="prototype" class="java.sql.Timestamp">
<constructor-arg value="#{ T(java.lang.System).currentTimeMillis()}" />
</bean>
We can use DB level methods to set the time like sysdate for oracle, but I am not keen on using DB specific methods in code for testing purposes(H2 DB used for testing)
Any Help is greatly appreciated
Thanks
I had the same issue, the problem is that :timestamp expression is evaluated as a Collection Projection, check the code here.
So my Original query was something like this
update table set status = 1, published_at = :now where id_event in (:id)
After the parsing was something like this
update table set status = 1, published_at = ?, ?, ? where id_event in (?, ?, ?)
The numbers of ? is the same as the number of results from the select statement. So if the result is more than one, you get a Bad Grammar exception.
I made a not very nice solution (intrusive) using spring-integration-java-dsl
protected void addNotCollectionProjectionExpression(
ExpressionEvaluatingSqlParameterSourceFactory factory,
String key, String expression) {
try {
Field parameterExpressionsField = factory.getClass().getDeclaredField("parameterExpressions");
parameterExpressionsField.setAccessible(true);
Map<String, Expression[]> parameterExpressions = (Map<String, Expression[]>) parameterExpressionsField
.get(factory);
Field parserField = factory.getClass().getDeclaredField("PARSER");
parserField.setAccessible(true);
ExpressionParser parser = (ExpressionParser) parserField.get(factory);
Expression compiledExpression = parser.parseExpression(expression);
Expression[] expressions = new Expression[]{
compiledExpression,
compiledExpression
};
parameterExpressions.put(key, expressions);
} catch (NoSuchFieldException | IllegalAccessException e) {
logger.error("Field parameterExpressions | PARSER can not be obtained", e);
}
}
....
//how to use it
ExpressionEvaluatingSqlParameterSourceFactory factory =
new ExpressionEvaluatingSqlParameterSourceFactory();
addNotCollectionProjectionExpression(factory, "now",
"T(com.example.MyClass).staticMethod()");
return factory;
You can notice that I am avoiding to use Collection Projection using the same expression in both elements of the array.

NHibernate one-to-one mapping with unique foreign key

I'm seeing some unexpected query behavior with a one-to-one mapping where the "child" record is possibly null. Nhibernate seems to generate inner joins when it seems like a left join would be more appropriate. Given the following schema:
CREATE TABLE [dbo].[Customer](
[CustomerId] [int] IDENTITY(1,1) NOT NULL,
[PersonId] [int] NOT NULL,
[AccountNumber] [int] NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Employee](
[EmployeeId] [int] IDENTITY(1,1) NOT NULL,
[PersonId] [int] NOT NULL,
[Title] [varchar](50) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
(
[EmployeeId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED
(
[PersonId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Customer] WITH CHECK ADD CONSTRAINT [FK_Customer_Person] FOREIGN KEY([PersonId])
REFERENCES [dbo].[Person] ([PersonId])
GO
ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_Person]
GO
ALTER TABLE [dbo].[Employee] WITH CHECK ADD CONSTRAINT [FK_Employee_Person] FOREIGN KEY([PersonId])
REFERENCES [dbo].[Person] ([PersonId])
GO
ALTER TABLE [dbo].[Employee] CHECK CONSTRAINT [FK_Employee_Person]
GO
And the following classes:
namespace OneToOneMapping.Model
{
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Customer Customer { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
public virtual System.Int32 Id { get; set; }
public virtual Person Person { get; set; }
public virtual string Title { get; set; }
}
public class Customer
{
public virtual System.Int32 Id { get; set; }
public virtual Person Person { get; set; }
public virtual int AccountNumber { get; set; }
}
}
And mappings:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="OneToOneMapping"
namespace="OneToOneMapping.Model">
<class name="Person" table="Person" lazy="true" >
<id name="Id" column="PersonId">
<generator class="identity"/>
</id>
<one-to-one name="Customer" cascade="all-delete-orphan" property-ref="Person" class="OneToOneMapping.Model.Customer" />
<one-to-one name="Employee" cascade="all-delete-orphan" property-ref="Person" class="OneToOneMapping.Model.Employee" />
<property name="Name"/>
</class>
<class name="OneToOneMapping.Model.Customer, OneToOneMapping" table="Customer">
<id name="Id" column="CustomerId">
<generator class="identity"/>
</id>
<property name="AccountNumber"/>
<many-to-one name="Person" class="Person" column="PersonId" unique="true" cascade="save-update"/>
</class>
<class name="OneToOneMapping.Model.Employee, OneToOneMapping" table="Employee" lazy="true" mutable="true">
<id name="Id" column="EmployeeId">
<generator class="identity"/>
</id>
<property name="Title"/>
<many-to-one name="Person" class="Person" column="PersonId" unique="true" cascade="save-update"/>
</class>
</hibernate-mapping>
I am unable to create a linq query where I can return a projection indicating whether a Customer is also an Employee (or vice versa). Running something like the following statement:
var t = Session.Query<Customer>().Select(c => new { AccountNumber = c.AccountNumber, Name= c.Person.Name, IsEmployee = c.Person.Employee.Id != null }).ToList();
Generates the following SQL (note the "where" clause prevents any Customer records that don't have also have an associated Employee record from returning at all):
SELECT customer0_.AccountNumber AS col_0_0_
,person1_.NAME AS col_1_0_
,employee2_.EmployeeId AS col_2_0_
FROM Customer customer0_
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId
,Employee employee2_
WHERE person1_.PersonId = employee2_.PersonId
I'd expect it to generate something like the following to just return a null EmployeeId when the record does not exist:
SELECT customer0_.AccountNumber AS col_0_0_
,person1_.NAME AS col_1_0_
,employee2_.EmployeeId AS col_2_0_
FROM Customer customer0_
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId
Left Join Employee employee2_ on person1_.PersonId = employee2_.PersonId
Am I missing something or is this a known issue with the "one-to-one" mapping?
We've identified a different query method using subqueries in the projections that seems to work. I believe the underlying issue is related to the open issue here: https://nhibernate.jira.com/browse/NH-3117?jql=text%20~%20%22one%20to%20one%22. It appears that the NHibernate linq provider doesn't properly handle one-to-one mappings where the referencing item(s) possibly doesn't exist.
The workaround:
var t = Session.Query<Customer>()
.Select(c => new {
AccountNumber = c.AccountNumber,
Name= c.Person.Name,
IsEmployee = Session.Query<Employee>().Any(e => e.Id == c.Person.Id)
}).ToList();
Generates the following SQL:
SELECT customer0_.AccountNumber AS col_0_0_
,person1_.NAME AS col_1_0_
,CASE
WHEN EXISTS (
SELECT employee2_.EmployeeId
FROM Employee employee2_
WHERE employee2_.EmployeeId = customer0_.PersonId
)
THEN 1
ELSE 0
END AS col_2_0_
FROM Customer customer0_
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId

How do I work around the N+1 issue with one-to-one mapping in NHibernate?

I have a Task entity which has a preceding and following task:
namespace OneToOneIssue.Domain
{
public class Task
{
public virtual Guid Id { get; set; }
public virtual string Description { get; set; }
public virtual Task FollowingTask { get; set; }
public virtual Task PrecedingTask { get; set; }
}
}
The database table looks like this:
CREATE TABLE [dbo].[Task](
[Id] uniqueidentifier NOT NULL,
[Description] nvarchar(100) NULL,
[FollowingTaskId] uniqueidentifier NULL
CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('30efbfda-f3b5-42fb-906e-098fb32be79d', 'Task 1', 'f7367187-406d-47db-931e-b9e4fa8a4774')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('f7367187-406d-47db-931e-b9e4fa8a4774', 'Task 2', '42c25da5-7c04-4adc-a9c2-6bf8a9ff5c89')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('42c25da5-7c04-4adc-a9c2-6bf8a9ff5c89', 'Task 3', NULL)
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('ffe58f51-bb85-4681-af9d-d232326a30e4', 'Task 4', 'ba2ee26c-ebbb-4d7e-a596-40db9f0711c4')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('ba2ee26c-ebbb-4d7e-a596-40db9f0711c4', 'Task 5', '29189134-8be9-4d93-873e-ce5efefe1c1a')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('29189134-8be9-4d93-873e-ce5efefe1c1a', 'Task 6', NULL)
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('ef069d0a-f2a8-4c9a-8bbc-99ee1e0e2991', 'Task 7', '56a6eb57-ab9f-49cb-875a-a072158b0265')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('56a6eb57-ab9f-49cb-875a-a072158b0265', 'Task 8', 'f8b7cc9b-269e-44c7-85bf-44592d70a21e')
INSERT INTO [Task] ([Id], [Description], [FollowingTaskId]) VALUES ('f8b7cc9b-269e-44c7-85bf-44592d70a21e', 'Task 9', NULL)
and the mapping like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="OneToOneIssue.Domain"
assembly="OneToOneIssue.Domain">
<class name="Task" table="`Task`">
<id name="Id" column="Id" type="guid">
<generator class="assigned"/>
</id>
<property name="Description" column="`Description`" />
<many-to-one name="FollowingTask" class="Task" column="FollowingTaskId" />
<one-to-one name="PrecedingTask" class="Task" property-ref="FollowingTask" />
</class>
</hibernate-mapping>
Now if I run a query to get tasks 2, 5 and 8 (the only ones that have a preceding and following task like this:
var tasks = session
.CreateCriteria<Task>()
.Add(Restrictions.In("Description", new string[] {"Task 2", "Task 5", "Task 8"}))
.List<Task>();
Then I get the main query I would expect but also a query each for all of the preceding tasks:
exec sp_executesql N'SELECT this_.Id as Id0_1_, this_.[Description] as Descript2_0_1_, this_.FollowingTaskId as Followin3_0_1_, task2_.Id as Id0_0_, task2_.[Description] as Descript2_0_0_, task2_.FollowingTaskId as Followin3_0_0_ FROM [Task] this_ left outer join [Task] task2_ on this_.Id=task2_.FollowingTaskId WHERE this_.[Description] in (#p0, #p1, #p2)',N'#p0 nvarchar(4000),#p1 nvarchar(4000),#p2 nvarchar(4000)',#p0=N'Task 2',#p1=N'Task 5',#p2=N'Task 8'
exec sp_executesql N'SELECT task0_.Id as Id0_1_, task0_.[Description] as Descript2_0_1_, task0_.FollowingTaskId as Followin3_0_1_, task1_.Id as Id0_0_, task1_.[Description] as Descript2_0_0_, task1_.FollowingTaskId as Followin3_0_0_ FROM [Task] task0_ left outer join [Task] task1_ on task0_.Id=task1_.FollowingTaskId WHERE task0_.FollowingTaskId=#p0',N'#p0 uniqueidentifier',#p0='FFE58F51-BB85-4681-AF9D-D232326A30E4'
exec sp_executesql N'SELECT task0_.Id as Id0_1_, task0_.[Description] as Descript2_0_1_, task0_.FollowingTaskId as Followin3_0_1_, task1_.Id as Id0_0_, task1_.[Description] as Descript2_0_0_, task1_.FollowingTaskId as Followin3_0_0_ FROM [Task] task0_ left outer join [Task] task1_ on task0_.Id=task1_.FollowingTaskId WHERE task0_.FollowingTaskId=#p0',N'#p0 uniqueidentifier',#p0='EF069D0A-F2A8-4C9A-8BBC-99EE1E0E2991'
exec sp_executesql N'SELECT task0_.Id as Id0_1_, task0_.[Description] as Descript2_0_1_, task0_.FollowingTaskId as Followin3_0_1_, task1_.Id as Id0_0_, task1_.[Description] as Descript2_0_0_, task1_.FollowingTaskId as Followin3_0_0_ FROM [Task] task0_ left outer join [Task] task1_ on task0_.Id=task1_.FollowingTaskId WHERE task0_.FollowingTaskId=#p0',N'#p0 uniqueidentifier',#p0='30EFBFDA-F3B5-42FB-906E-098FB32BE79D'
I don't even need the preceding task loaded.
Eager loading the preceding task does not help either.
It does not help if I set the FollowingTask and PrecedingTask to lazy="proxy". It does solve the problem if remove the one-to-one mapping, but this is not a possible solution.
I have seen questions on here about getting round the problem if you need the mapped one-to-one and the answer seems to be that it's a bug and you just have to live with it. However, I do not even need it loaded. I never access it, it just loads for no reason. I believe this question is also unique because of the recursive nature of the relationship.
Is there any way I can prevent it loading or is there another way altogether to achieve the same thing?
Just a suggestion, how to change the mapping. The table structure fits to parent-child schema. We only know that you'd like to have exactly one child.
For parent-child has NHibernate strong support and you won't experience loading of related children any more. So, Instead of <ont-to-one map it like this. c#:
public class Task
{
public virtual Task FollowingTask { get; set; }
public virtual IList<Task> PrecedingTasks { get; set; }
}
mapping hbm:
<many-to-one name="FollowingTask" class="Task" column="FollowingTaskId" />
<bag name="PrecedingTasks">
<key column="FollowingTaskId"></key>
<one-to-many class="Task"/>
</bag>
Now we can even make the IList<Task> PrecedingTasks protected and create Task PrecedingTask property which will handle the get/set access to the first PrecedingTasks list element

iBatis.NET to insert record with Oracle stored procedure, return record ID

I am attempting to insert a record in an Oracle table with a Function, which would be called through iBatis.NET. Function works as expected in Oracle when called directly.
I have tried using <statement> and <insert> SqlMap but I can't get iBatis.NET to call the function, and Oracle doesn't support returning anything from Stored Procedure.
I would need to pass properties of my object as parameters to a function/sproc and get back the ID of this new record.
What would be a good combination of iBatis.NET call / SQLMap / Sproc or Function signature in Oracle?
The documentation only has examples of in-line SQL and I can only use sprocs.
Due to the number of properties in real objects, the hash-map and number of parameters is in the 30+.
Ideally I would be able to do this (doesn't work):
<procedure id="InsertPerson" parameterClass="BOM.Person">
TestDB.PERSON_PKG.InsertPerson(#Name#, #Age#)
</procedure>
Domain object:
public class Person
{
int ID { get; set; }
string Name { get; set; }
decimal Age { get; set; }
}
iBatis.NET call:
int personID = mapper.Insert("InsertPerson", person);
Oracle Stored Procedure:
FUNCTION InsertPerson(
p_Name IN Persons.Name%TYPE,
p_Age IN Persons.Age%TYPE,
) RETURN NUMBER
IS
NEW_ID Persons.ID%TYPE;
BEGIN
SELECT Persons_SEQ.NEXTVAL INTO NEW_ID FROM DUAL; /* Get new ID*/
INSERT INTO Persons(ID, Name, Age)
SELECT NEW_ID, p_Name, p_Age from dual; /* Insert record */
COMMIT;
RETURN NEW_ID;
END;
In case this helps someone else, I was unable to find a workaround to my problem.
I ended up implementing this as a Stored Procedure which takes input parameters for all fields to be inserted in to a table, and one output parameter which returns the unique ID generated by sequence.
After executing mapper.Insert(...) I simply read the output parameter and return it.
C#:
mapper.BeginTransaction(System.Data.IsolationLevel.Serializable);
// Add new Record
Hashtable param = new Hashtable();
param.Add("ID", user.ID); // Output
param.Add("DeptID", user.DeptID);
param.Add("RightID", user.RightID);
mapper.Insert("AddUserRight", param);
user.ID = Convert.ToInt32(param["ID"]);
MyBATIS map:
<?xml version="1.0" encoding="utf-8" ?>
<sqlMap namespace="CCP" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<statements>
<procedure id="AddUserRight" parameterMap="AddUserRight-param">
Database.USER_PKG.ADDUSERRIGHT
</procedure>
</statements>
<parameterMaps>
<parameterMap id="AddUserRight-param">
<parameter property="ID" column="ID" direction="Output" />
<parameter property="DeptID" column="DeptID" direction="Input" />
<parameter property="RightID" column="RightID" direction="Input" />
</parameterMap>
</parameterMaps>
</sqlMap>
Sproc (Oracle):
PROCEDURE AddUserRight(
ID OUT USERRIGHTS.USERID%TYPE,
DEPTID IN USERRIGHTS.DEPTID%TYPE,
RIGHTID IN USERRIGHTS.RIGHTID%TYPE)
IS
BEGIN
SELECT USERRIGHTS_UNQ_SEQ.NEXTVAL INTO ID FROM DUAL;
INSERT INTO USERRIGHTS(ID, DEPTID, RIGHTID)
VALUES (ID, DEPTID, RIGHTID);
END;

Resources