My project is a Database center, like PLSQL but used in a web browser. Sometimes I need to create or alter a table, but I don't know if Mybatis supports DDL, and I haven't found any documents about this.
For the most part DDL works just like DML using mybatis. The one difference is that you will need to use ${} instead of #{} for parameters. Most databases do not support prepared statements with DDL. The $ notation is a string substitution rather than a parameter for a prepared statement.
<update id="exchangePartition" parameterType="java.util.Map">
alter table ${destinationTableName}
exchange partition ${destinationPartitionName}
with table ${sourceTableName}
including indexes
with validation
</update>
It is also helpful to know the call syntax with the statement type callable to invoke stored procedures.
<update id="gatherStatistics" statementType="CALLABLE" parameterType="Map">
{call
dbms_stats.gather_table_stats(
ownname => #{tableOwner},
tabname => #{tableName}
<if test="partitionName != null">
, partname => #{partitionName}
</if>
)
}
</update>
MyBatis supports any native SQL/PlSql commands.
So yes, it supports DDL statements.
Related
In Mybatis, we use <selectedKey> for generated column when define the insert statement, but for different DB it has different content in selectedKey element.
For MySQL like this:
<selectKey resultType="java.lang.Long" order="AFTER" keyProperty="productId">
SELECT LAST_INSERT_ID()
</selectKey>
For Oracle like this:
<selectKey resultType="java.math.BigDecimal" order="BEFORE" keyProperty="id">
SELECT U_USER_INFO_SEQ.Nextval as ID from DUAL </selectKey>
So how this support multiple DataSource?
You can try using http://www.mybatis.org/mybatis-3/configuration.html#databaseIdProvider.
Doing so allows you to use _databaseId as described http://www.mybatis.org/mybatis-3/dynamic-sql.html#Multi-db_vendor_support
<insert id="insert" parameterType="com.youneverwalkalone.cent.web.model.Category" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
LOCK TABLE t_category WRITE;
UPDATE t_category SET rgt = rgt + 2 WHERE rgt greater than #{parentNode.lft,jdbcType=BIGINT};
UPDATE t_category SET lft = lft + 2 WHERE lft greater than #{parentNode.lft,jdbcType=BIGINT};
insert into t_category (
name, lft, rgt,
time_created, people_created,
state, type, project)
values (
#{record.name,jdbcType=VARCHAR}, #{parentNode.lft,jdbcType=BIGINT}+1, #{parentNode.lft,jdbcType=BIGINT}+2,
#{record.timeCreated,jdbcType=TIMESTAMP}, #{record.peopleCreated,jdbcType=BIGINT},
#{record.state,jdbcType=SMALLINT},#{record.type,jdbcType=VARCHAR},#{record.project,jdbcType=VARCHAR});
UNLOCK TABLES;
</insert>
Above is my code snippet. Call this insert method will get errors.
My question:
1) Does mybatis supourt these grammar--multiple sql in one method?
2) If not support, how to handle this case.
1/ it is actually not related to Mybatis, if JDBC supports it and used DB does (as well as the driver) then yes you can do that with Mybatis.
As noticed by Gabriele Coletta the question MyBatis executing multiple sql statements in one go, is that possible? contains the answer.
As you will see, the syntax is different across database types (mysql, ms-sql, oracle)
2/ without object since answer to 1/ is yes.
I am building an Ecto query like this:
from item in query,
where: like(item.description, ^"%#{text}%")
I'm concerned that this allows SQL injection in text. Before trying to fix that, I want to see how the query is actually sent to the database.
If I inspect the query or look at what is logged, I see some SQL, but it's not valid.
For instance, inspecting the query shows me this:
{"SELECT i0.\"id\", i0.\"store_id\", i0.\"title\", i0.\"description\"
FROM \"items\" AS i0 WHERE (i0.\"description\" LIKE $1)",
["%foo%"]}
When I pass this query to Repo.all, it logs this:
SELECT i0."id", i0."store_id", i0."title", i0."description"
FROM "items" AS i0 WHERE (i0."description" LIKE $1) ["%foo%"]
But if I copy and paste that into psql, PostgreSQL gives me an error:
ERROR: 42P02: there is no parameter $1
It seems as though Ecto may actually be doing a parameterized query, like this:
PREPARE bydesc(text) AS SELECT i0."id",
i0."store_id", i0."title", i0."description"
FROM "items" AS i0 WHERE (i0."description" LIKE $1);
EXECUTE bydesc('foo');
If so, I think that would prevent SQL injection. But I'm just guessing that this is what Ecto does.
How can I see the actual SQL that Ecto is executing?
Ecto uses only prepared statements. When using ecto query syntax, introducing SQL injection is not possible. The query syntax verifies at compile-time that no SQL injection is possible.
Showing exactly the queries executed might be difficult because of couple reasons:
Postgrex (and hence Ecto) uses the postgresql binary protocol (instead of the most common, but less efficient, text protocol), so the PREPARE query never actually exists as a string.
For most cases all you would see would be one initial PREPARE 64237612638712636123(...) AS ... and later a lot of EXECUTE 64237612638712636123(...) which isn't that helpful. Trying to relate one to another would be horrible.
From my experience most software of that kind, use prepare statements and log them instead of raw queries, since it's much more helpful in understanding the behaviour of the system.
Yes, that is the exact SQL that is being executed by Ecto (it uses prepared queries through the db_connection package internally) and no SQL injection is possible in that code. This can be verified by turning on logging of all executed SQL queries by changing log_statement to all in postgresql.conf:
...
log_statement = 'all'
...
and then restarting PostgreSQL and running a query. For the following queries:
Repo.get(Post, 1)
Repo.get(Post, 2)
this is logged:
LOG: execute ecto_818: SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = $1)
DETAIL: parameters: $1 = '1'
LOG: execute ecto_818: SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = $1)
DETAIL: parameters: $1 = '2'
I am trying to ues JDBC PreparedStatement to execute a SQL statement which has a question mark ? as operator. (It's the hstore ?| operator)
It throws an exception tells me that I have not specified the parameter org.postgresql.util.PSQLException: No value specified for parameter 1.. Which it should be the operator. Is there any way to by pass the parameter check and execute the statement directly?
I have seen database feature in IntelliJ can directly execute SQL with JDBC driver, I think there should be a way
There is an question (Escaping hstore contains operators in a JDBC Prepared statement) about this issue, but I really need this operator to make index working on large data sets.
Thank you
You can use SQL function inlining. A simple SQL function will get rewritten (almost always).
CREATE OR REPLACE FUNCTION hstore_contains(hstore, text[]) RETURNS boolean AS $$
SELECT $1 ?| $2;
$$ LANGUAGE SQL;
So the two queries below will get identical query plan and will both take advantage of indexes:
SELECT * FROM tbl WHERE hstore_contains(col1,array['a','b']);
SELECT * FROM tbl WHERE col1 ?| array['a','b'];
I'm attempting to do an upsert-style statement for Postgres in jOOQ. The framework I'm running in takes care of concurrency concerns in this specific situation so I'm not worried about that. I'm only using jOOQ for creating the SQL, the actual execution is done via Spring's JdbcTemplate and a BeanPropertySqlParameterSource.
I've decided to go with a two-step "insert..where not exists" / "update .." process.
My SQL is:
insert into mytable (my_id, col1, col2) select :myId,
:firstCol, :secondCol where not exists (select 1
from mytable where my_id = :myId)
I'm using Postgres 9.4, jOOQ 3.5. I'm not sure how to express both the jOOQ params in the select and the "where not exists" clause in jOOQ.
Suggestions to change programming languages or databases aren't viable in my situation.
If you want to reuse a named parameter in jOOQ, ideally, you create the AST element outside of the query, as such:
// Assuming a static import
import static org.jooq.impl.DSL.*;
Param<Integer> myId = param("myId", Integer.class);
You can then use it multiple times in your query:
using(configuration)
.insertInto(MY_TABLE, MY_TABLE.MY_ID, MY_TABLE.COL1, MY_TABLE.COL2)
.select(
select(
myId,
param("firstCol", MY_TABLE.COL1.getType()),
param("secondCol", MY_TABLE.COL2.getType())
)
.whereNotExists(
selectOne()
.from(MY_TABLE)
.where(MY_TABLE.MY_ID.eq(myId))
)
);