package org.easyframe.tutorial.lesson7;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.easyframe.tutorial.lesson2.entity.Student;
import org.easyframe.tutorial.lesson4.entity.Person;
import org.easyframe.tutorial.lesson4.entity.School;
import org.easyframe.tutorial.lesson5.entity.Item;
import org.easyframe.tutorial.lesson7.entity.NodeTable;
import org.junit.BeforeClass;
import org.junit.Test;
import jef.common.wrapper.IntRange;
import jef.common.wrapper.Page;
import jef.database.DbClient;
import jef.database.DbClientBuilder;
import jef.database.NativeQuery;
import jef.database.ORMConfig;
import jef.database.Session;
import jef.database.SqlTemplate;
import jef.database.Transaction;
import jef.database.query.Func;
import jef.database.wrapper.ResultIterator;
import jef.database.wrapper.populator.Transformer;
import jef.tools.string.RandomData;
/**
* NativeQuery相关操作演示
* @author jiyi
*
*/
public class Case1 extends org.junit.Assert {
private static DbClient db;
@BeforeClass
public static void setup() throws SQLException {
db = new DbClientBuilder().setEnhancePackages("org.easyframe.tutorial").build();
ORMConfig.getInstance().setDebugMode(false);
db.dropTable(NodeTable.class, Person.class, Item.class, Student.class, School.class);
db.createTable(Person.class, Item.class, Student.class, School.class);
db.createTable(NodeTable.class);
Person p = new Person();
p.setGender('F');
p.setName("张飞");
db.insert(p);
p = new Person();
p.setGender('F');
p.setName("关羽");
db.insert(p);
p = new Person();
p.setGender('F');
p.setName("刘备");
db.insert(p);
Item item = new Item();
item.setName("张飞");
item.setCatalogyId(12);
db.insert(item);
Student st = new Student();
st.setName("张飞");
st.setDateOfBirth(new Date());
st.setGender("F");
st.setGrade("3");
db.insert(st);
st = new Student();
st.setName("关羽");
st.setDateOfBirth(new Date());
st.setGender("F");
st.setGrade("2");
db.insert(st);
ORMConfig.getInstance().setDebugMode(true);
}
/**
* 演示NativeQuery的两种来源,一种来自于配置,一种来自于代码中拼凑的SQL
*/
@Test
public void testNativeQuery() {
// 方法1 NamedQuery
{
NativeQuery<ResultWrapper> query = db.createNamedQuery("unionQuery-1", ResultWrapper.class);
List<ResultWrapper> result = query.getResultList();
System.out.println(result);
}
// 方法2 直接传入SQL
{
String sql = "select * from((select upper(t1.person_name) AS name, T1.gender, '1' AS GRADE," + "T2.NAME AS SCHOOLNAME from T_PERSON T1 inner join SCHOOL T2 ON T1.CURRENT_SCHOOL_ID=T2.ID"
+ ") union ( select t.NAME,t.GENDER,t.GRADE,'Unknown' AS SCHOOLNAME from STUDENT t )) a";
NativeQuery<ResultWrapper> query = db.createNativeQuery(sql, ResultWrapper.class);
List<ResultWrapper> result = query.getResultList();
System.out.println(result);
}
}
/**
* 演示NativeQuery的绑定变量参数和API用法
*/
@Test
public void testQueryParams() {
String sql = "select distinct(select grade from student s where s.name=person_name) grade,person_name,gender from t_person where id<:id";
NativeQuery<Map> query = db.createNativeQuery(sql, Map.class);
query.setParameter("id", 12);
// 自动改写为count语句进行查询
System.out.println("预计查出" + query.getResultCount() + "条结果");
// 查询多条结果
System.out.println(query.getResultList());
// 重新设置参数
System.out.println("=== 重新设置参数 ===");
query.setParameter("id", 2);
System.out.println("预计查出" + query.getResultCount() + "条结果");
System.out.println(query.getResultList());
// 查出第一条结果
System.out.println(query.getSingleOnlyResult());
}
/**
* 一个简单的POJO,作为存放查询结果的容器
*/
public static class ResultWrapper {
private String name;
private String gender;
private Integer grade;
private String schoolName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
/**
* 演示SChema重定向功能
*
* SQL语句中的usera userb都是不存在的schema,通过jef.properties中的配置,被重定向到APP schema下
*
* @throws SQLException
*/
@Test
public void testSchemaMapping() throws SQLException {
String sql = "select * from usera.t_person union all select * from userb.t_person";
db.createNativeQuery(sql).getResultList();
}
/**
* concat(person_name, gender) 在实际使用时会改写为 person_name||gender
*/
@Test
public void testRewrite1() throws SQLException {
String sql = "select concat(person_name, gender) from usera.t_person";
System.out.println(db.createNativeQuery(sql).getResultList());
}
/**
* 本例演示 replace、 decode、nvl等函数在Derby上的效果
*
* @throws SQLException
*/
@Test
public void testRewrite2() throws SQLException {
String sql = "select replace(person_name,'张','王') person_name,decode(nvl(gender,'M'),'M','男','女') gender from t_person";
System.out.println(db.createNativeQuery(sql).getResultList());
}
/**
* 本例演示时间日期函数被Derby方言重写后的效果
*
* @throws SQLException
*/
@Test
public void testRewrite3() throws SQLException {
// 获得:当前日期减去1个月,和学生生日相差的天数。
String sql = "select datediff(add_months(sysdate, -1), DATE_OF_BIRTH),DATE_OF_BIRTH from student";
System.out.println(db.createNativeQuery(sql).getResultList());
// 获得:在当前日期上加上1年
sql = "select addDate(sysdate, INTERVAL 1 YEAR) from student";
System.out.println(db.createNativeQuery(sql).getResultList());
}
/**
* 本例演示在非Oracle数据库上支持递归查询
*
* @throws SQLException
*/
@Test
public void testStartWithConnectBy() throws SQLException {
// 准备一些数据
db.truncate(NodeTable.class);
List<NodeTable> data = new ArrayList<NodeTable>();
data.add(new NodeTable(1, 0, "-Root"));
data.add(new NodeTable(2, 1, "水果"));
data.add(new NodeTable(5, 2, " 西瓜"));
data.add(new NodeTable(10, 2, " 葡萄"));
data.add(new NodeTable(4, 2, " 苹果"));
data.add(new NodeTable(8, 4, " 青涩的苹果"));
data.add(new NodeTable(12, 4, " 我的小苹果"));
data.add(new NodeTable(3, 1, "家电"));
data.add(new NodeTable(6, 3, " 电视机"));
data.add(new NodeTable(7, 3, " 洗衣机"));
data.add(new NodeTable(11, 6, " 彩色电视机"));
db.batchInsert(data);
String sql = "select * from nodetable t START WITH t.id IN (4,6) CONNECT BY PRIOR t.id = t.pid";
NativeQuery<NodeTable> query = db.createNativeQuery(sql, NodeTable.class);
List<NodeTable> result = query.getResultList();
for (NodeTable p : result) {
System.out.println(p);
}
}
/**
* 本例演示Translate函数在Derby上的效果
*
* @throws SQLException
*/
@Test
public void testTranslate() throws SQLException {
String sql = "select translate(person_name,'张刘关','刘关张') from t_person";
System.out.println(db.createNativeQuery(sql).getResultList());
}
/**
* 绑定变量中使用Like条件,通过在SQL中指定参数类型使查询支持模糊匹配。
*/
@Test
public void testLike() {
String sql = "select * from t_person where person_name like :name<$string$>";
System.out.println(db.createNativeQuery(sql).setParameter("name", "张").getResultList());
}
/**
* 动态表达式省略
*/
@Test
public void testDynamicSQL() {
// SQL语句中写了四个查询条件
String sql = "select * from t_person where id=:id " + "and person_name like :person_name<$string$> " + "and currentSchoolId=:schoolId " + "and gender=:gender";
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
{
System.out.println("== 按ID查询 ==");
query.setParameter("id", 1);
Person p = query.getSingleResult();
System.out.println(p.getId());
System.out.println(p);
}
{
System.out.println("== 由于参数'ID'并未清除,所以变为 ID + NAME查询 ==");
query.setParameter("person_name", "张");
System.out.println(query.getResultList());
}
{
System.out.println("== 参数清除后,只传入NAME,按NAME查询 ==");
query.clearParameters();
query.setParameter("person_name", "张");
System.out.println(query.getResultList());
}
{
System.out.println("== 按NAME+GENDER查询 ==");
query.setParameter("gender", "F");
System.out.println(query.getResultList());
}
{
System.out.println("== 一个参数都没有,变为查全表 ==");
query.clearParameters();
System.out.println(query.getResultList());
}
}
/**
* 动态表达式省略 (IN条件)
*/
@Test
public void testDynamicSQL2() {
String sql = "select * from t_person where id not in (:ids)";
System.out.println(db.createNativeQuery(sql, Person.class).getResultList());
}
/**
* 动态表达式省略的失效——将参数值设置为null,并不能起到清空参数的作用
*/
@Test
public void testDynamicSQL3() {
String sql = "select * from t_person where id not in (:ids)";
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
// 将参数值设置为null,并不能起到清空参数的作用
query.setParameter("ids", null);
System.out.println(query.getResultList());
}
/**
* 动态表达式省略——不仅仅是where条件可以省略,update中的赋值表达式也可以省略
*/
@Test
public void testDynamicSQL4() {
String sql = "update t_person set person_name=:new_name, current_school_id=:new_schoold_id,gender=:new_gender where id=:id";
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
query.setParameter("new_name", "孟德");
query.setParameter("id", 1);
int count = query.executeUpdate();
System.out.println(count);
}
/**
* 动态SQL片段支持
*/
@Test
public void testDynamicSqlExpression() {
String sql = "select :columns<sql> from t_person where " + "id in (:ids<int>) and person_name like :person_name<$string$> " + "order by :orders<sql>";
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
// 查询哪些列、按什么列排序,都是在查询创建以后动态指定的。
query.setParameter("columns", "id, person_name, gender");
query.setParameter("orders", "gender asc");
System.out.println(query.getResultList());
// 动态SQL片段和动态表达式省略功能混合使用
query.setParameter("ids", new int[] { 1, 2, 3 });
query.setParameter("columns", "person_name, id + 1000 as id");
System.out.println(query.getResultList());
}
@Test
public void testNativeQueryPage() throws SQLException {
// SQL语句中写了四个查询条件
String sql = "select * from t_person where id=:id " + "and person_name like :person_name<$string$> " + "and currentSchoolId=:schoolId " + "and gender=:gender";
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
query.setParameter("gender", 'F');
// 每页5条,跳过最前面的2条记录
Page<Person> page = db.pageSelect(query, 5).setOffset(2).getPageData();
System.out.println("总共:" + page.getTotalCount() + " " + page.getList());
}
/**
* 在命名查询中为不同数据库分别配置了SQL的场景下,会自动选择适合当前数据库的SQL进行操作
*
* @throws SQLException
*/
@Test
public void testNativeQueryPage2() throws SQLException {
NativeQuery<Person> query = db.createNamedQuery("testOracleTree");// #oracle
if (query.containsParam("value")) { // 检查SQL是否需要该参数
query.setParameter("value", 100);
}
System.out.println(query.getResultList());
}
/**
* 使用原生SQL语句进行查询
*
* @throws SQLException
*/
@Test
public void testRawSQL() throws SQLException {
// 普通的原生SQL查询
String sql = "select id, person_name,gender from t_person";
{
List<Person> result = db.selectBySql(sql, Person.class);
System.out.println(result);
assertEquals(3, result.size());
}
// 限定结果范围——分页
{
List<Person> result = db.selectBySql(sql, new Transformer(Person.class), new IntRange(2, 3));
System.out.println(result);
assertEquals(2, result.size());
}
// 使用绑定变量
{
sql = "select * from t_person where person_name like ? or gender=?";
List<Person> result = db.selectBySql(sql, Person.class, "刘", "F");
System.out.println(result);
assertEquals(3, result.size());
}
// 执行原生SQL
{
sql = "insert into t_person(person_name,gender,created) values(?,?,current_timestamp)";
db.executeSql(sql, "曹操", "M");
db.executeSql(sql, "郭嘉", "M");
assertEquals(5, db.getSqlTemplate(null).countBySql("select count(*) from t_person"));
}
}
/**
* 无表查询
*/
@Test
public void testExpressionValue() throws SQLException {
// 传入复杂表达式时,其函数和语法会被改写
String s = "'今天是'||str(cast(year(sysdate)/100+1 as int))||'世纪'";
assertEquals("今天是21世纪", db.getExpressionValue(s, String.class));
// 要在某个特定数据库上执行无表查询,可以用SqlTemplate
SqlTemplate t = db.getSqlTemplate(null);
// 直接传入数据库函数
Date dbTime = t.getExpressionValue(Func.current_timestamp, Date.class);
System.out.println("当前时间为:" + dbTime);
}
/**
* 补充测试,支持limit关键字改写为当前数据库的分页
*/
@Test
public void testLimitKeyword() throws SQLException {
Transaction db=this.db.startTransaction();
insertMore(10,db);
String sql = "select id, person_name,gender from t_person order by person_name limit 8 offset 4";
{
NativeQuery<Person> query=db.createNativeQuery(sql, Person.class);
assertEquals(8, query.getResultCount());
assertEquals(8, query.getResultList().size());
ResultIterator<Person> it = db.createNativeQuery(sql, Person.class).getResultIterator();
int count=0;
for (; it.hasNext();) {
it.next();
count++;
}
it.close();
assertEquals(8, count);
}
{
System.out.println("=========== Normal");
NativeQuery<Person> query = db.createNativeQuery("select id, person_name,gender from t_person order by person_name", Person.class);
query.setRange(new IntRange(6, 10));
assertEquals(13, query.getResultCount());
List<Person> list=query.getResultList();
assertEquals(5, list.size());
//特性:Range设定不会影响getResultSet()方法, API文档已修订
}
{
//案例3,当limit和range同时指定后,range使用数据库分页,limit被SQL上移除,但会补做一次内存分页。(需要用NativeQuery测试)
System.out.println("=========== add range 1,2");
System.out.println(sql);
NativeQuery<Person> query = db.createNativeQuery(sql, Person.class);
assertEquals(8, query.getResultCount());
System.out.println("Range set");
query.setRange(new IntRange(3, 3));
assertEquals(8, query.getResultCount()); //设置Range,不影响结果
System.out.println("进行了双重过滤,原先的SQL语句过滤了前4条,这里又过滤了两条,相当于过滤了7条。");
assertEquals(1, query.getResultList().size());
}
db.rollback();
db.close();
}
private void insertMore(int max,Session session) throws SQLException {
List<Person> ps=new ArrayList<Person>();
for(int i=0;i<max;i++){
Person p = new Person();
p.setGender('M');
p.setCurrentSchoolId(1);
p.setCreated(new Date());
p.setName(RandomData.randomChineseName());
ps.add(p);
}
session.batchInsert(ps);
}
}