Nutz自定义SQL
创建SQL对象
用户可以硬编码 SQL 语句,比如
Sql sql = Sqls.create("DELETE FROM t_abc WHERE name='Peter'");
支持占位符的书写方式,比如
Sql sql = Sqls.create("DELETE FROM $table WHERE name=@name");
sql.vars().set("table","t_abc");
sql.params().set("name","Peter");
// 连写
sql.setVar("table","t_abc").setVar(...);
sql.setParam("name","Peter").setParam(...);
- $table 将会被替换成 t_abc
- @name 将会被替换成 ?,用来创建 PreparedStatement
Sql 的逃逸字符
有些时候,有的朋友给出的 SQL 包括特殊字符 ‘@’ 或者 ‘$’,比如
Sql sql = Sqls.create("INSERT INTO t_usr (name,email) VALUES('XiaoMing','xiaoming@163.com');"
这个时候,因为有关键字 ‘@’,所以 SQL 不能被正确解析,因为你的本意是给一个 ‘xiaoming@163.com’ 这个字符串。但是 Nutz.Dao 却认为这个是个语句参数。
这时候你可以使用逃逸字符
Sql sql = Sqls.create("INSERT INTO t_usr (name,email) VALUES('XiaoMing','xiaoming@@163.com');"
即
- 输入 “@@” 表示一个 ‘@’
- 输入 "$ " 表 示 一 个 ′ " 表示一个 ' "表示一个′’
执行 Sql 对象
UPDATE, DELETE, INSERT
public void demoSql(Dao dao){
Sql sql = Sqls.create("SELECT name FROM t_abc WHERE name LIKE @name");
sql.params().set("name", "A%");
dao.execute(sql);
}
SELECT 是需要返回 结果的,于是,就需要你设置回调
在调用 dao.execute 方法前调用 sql.forceExecQuery 方法来强制让 nutz 用 select 方式运行该 sql 语句
List<String> demoSql(Dao dao) {
Sql sql = Sqls.create("SELECT name FROM t_abc WHERE name LIKE @name");
sql.params().set("name", "A%");
sql.setCallback(new SqlCallback() {
public Object invoke(Connection conn, ResultSet rs, Sql sql) throws SQLException {
List<String> list = new LinkedList<String>();
while (rs.next())
list.add(rs.getString("name"));
return list;
}
});
dao.execute(sql);
return sql.getList(String.class);
// Nutz内置了大量回调, 请查看Sqls.callback的属性
}
总结一下:
- 回调对象实现接口 org.nutz.dao.sql.SqlCallback,事实上,就像上例所示,这种场景非常适合使用匿名类。
- 你的回调函数的返回值会存放在 Sql 对象中
- 调用 sql.getResult() 可以直接返回这个对象
- sql.getList() 以及 sql.getObject() 方法会泛型安全的替你转型
- 如果你的对象类型符合要求,则直接返回,否则会通过 Nutz.Castors 替你转换。
- 对于 getList(),泛型参数用来描述集合内部元素的类型
- sql.getInt() 会安全的替你将结果转成 int,如果它可以被转成 int 的话,以下是我能想到的列表:
- 字符串
- 各种数字类型
- 字符
- 布尔类型
获取一个列表的回调
只要你保证你的 Master 类声明了 @Table
并且每个字段上的 @Column
可以同你的 ResultSet 配置起来 那么,上面的代码可以很方便的帮你获取一个 List<Master>
@Test
public void sql1() {
//创建SQL语句
Sql sql = Sqls.create("select * from student where name='小明'");
//获取回调
Sql setCallback = sql.setCallback(Sqls.callback.entities());
//重写父接口返回值 getEntity获取实体描述, 其中包含了Java Pojo<-->数据库的全部映射信息
Sql setEntity = sql.setEntity(dao.getEntity(Student.class));
//执行SQL语句
dao.execute(sql);
List<Student> list = sql.getList(Student.class);
}
批量执行
在 Nutz 1.b.38 之后的版本,自定义 SQL 可以支持批量操作
Sql sql = Sqls.create("UPDATE t_pet SET name=@name WHERE id=@id");
sql.params().set("name","XiaoBai").set("id",4);
sql.addBatch();//结束一个SQL
sql.params().set("name","XiaoHei").set("id",5);
sql.addBatch();//结束一个SQL
dao.execute(sql);
加载 SQL 文件
如何使用上述的 SQL 文件呢,可以将数个 SQL 文件加载到 Dao 对象中。在之后,只要得到 Dao 的对象,可以使用 dao.sqls() 方法获得 org.nutz.dao.SqlManager 接口,从这个接口中你就可以获得你预先定义好的 Sql 对象了。
对于 Dao 接口的默认实现, org.nutz.dao.impl.NutDao,提供两个方法,一个是通过构造函数,另一个是 setter 函数。
在构造时加载
(示意代码,新建NutDao属于重量级操作,应使用单例或Ioc模式)
Dao dao = new NutDao(datasource,new FileSqlManager("demo/sqls/all.sqls"));
System.out.println(dao.sqls().count());
在构造之后的任何时加载
(示意代码,新建NutDao属于重量级操作,应使用单例或Ioc模式)
Dao dao = new NutDao(datasource);
((NutDao)dao).setSqlManager(new FileSqlManager("demo/sqls/all.sqls"));
System.out.println(dao.sqls().count());
条件变量占位符
特殊的占位符 – $condition
Sql sql = Sqls.create("SELECT name FROM t_pet $condition");
sql.setCondition(Cnd.where("id", ">", 35)).setCallback(new SqlCallback() {
public Object invoke(Connection conn, ResultSet rs, Sql sql) throws SQLException {
List<String> list = new LinkedList<String>();
while (rs.next())
list.add(rs.getString("name"));
return list;
}
});
dao.execute(sql);
for (String name : sql.getList(String.class))
System.out.println(name);
获取实体的回调
Sql sql = Sqls.create("SELECT * FROM student $condition");
sql.setCallback(Sqls.callback.entity());
Entity<Student> entity = dao.getEntity(Student.class);
sql.setEntity(entity).setCondition(Cnd.wrap("id=1"));
dao.execute(sql);
Student object = sql.getObject(Student.class);
System.out.println("object==========="+object);
为了方便起见,你可以直接使用 Sqls.fetch 来创建你的 Sql 对象,这个函数会自动为你的 Sql 设置获取实体的回调
Sql sql = Sqls.fetchEntity("SELECT * FROM student $condition");
Entity<Student> entity = dao.getEntity(Student.class);
sql.setEntity(entity).setCondition(Cnd.wrap("id=1"));
dao.execute(sql);
Student object = sql.getObject(Student.class);
System.out.println("object==========="+object);
查询实体的回调
Sql sql = Sqls.create("SELECT * FROM student $condition");
sql.setCallback(Sqls.callback.entities());
Entity<Student> entity = dao.getEntity(Student.class);
sql.setEntity(entity).setCondition(Cnd.wrap("id=1"));
dao.execute(sql);
List<Student> list = sql.getList(Student.class);
System.out.println("list==========="+list);
多条件占位符
(1.b.53开始提供)
任意变量的值类型为Condition时,均自动识别为条件占位符
Sql sql = Sqls.create("select * from user where id in (select id from vips $vip_cnd ) and name in (select name from girls $girl_cnd )");
sql.setVar("vip_cnd", Cnd.where("level", ">", 5));
sql.setVar("girl_cnd", Cnd.where("age", "<", 30));
sql.setCallback(Sqls.callback.records());
dao.execute(sql);
注意事项: 如果调用过setEntity,那么对应的Cnd会进行属性名-字段名映射
练习
查询所有
@Autowired
private Dao dao;
@Override
public List<Goods_detail> DetailfindAll() {
List<Goods_detail> query = dao.query(Goods_detail.class, null);
return query;
}
通过ID查询
@Override
public Goods_detail DetailfindById(Long id) {
Sql sql = Sqls.create("select * from goods_detail $condition");
sql.setCallback(Sqls.callback.entity());
sql.setEntity(dao.getEntity(Goods_detail.class)).setCondition(Cnd.wrap("id="+id));
dao.execute(sql);
Goods_detail object = sql.getObject(Goods_detail.class);
System.out.println("object==========="+object);
return object;
}
多表查询
结合查询的条件创建的实体类Goods
@Override
public List<Goods> GoodsfindAll() {
//创建SQL语句
Sql sql = Sqls.create("select d.`id`,d.`sortId`,d.`name`,d.`address`,d.`price`,d.`createDate`,d.`remaining`,s.`name` as sname from goods_detail d,goods_sort s where d.`sortId`=s.`id`");
//获取回调
Sql setCallback = sql.setCallback(Sqls.callback.entities());
//重写父接口返回值 getEntity获取实体描述, 其中包含了Java Pojo<-->数据库的全部映射信息
Sql setEntity = sql.setEntity(dao.getEntity(Goods.class));
//执行SQL语句
dao.execute(sql);
List<Goods> list = sql.getList(Goods.class);
System.out.println(list);
return list;
}
insert插入
@Override
public void DetailInsert(Goods_detail goods_detail) {
Goods_detail insert = dao.insert(goods_detail);
System.out.println("insert============="+insert);
}
Delete删除
@Override
public void DetailDelete(Long id) {
int delete = dao.delete(Goods_detail.class,id);
}
Update更新
@Override
public void DetailUpeate(Goods_detail goods_detail) {
//根据需要修改的id获取当前对象信息
Goods_detail detail = dao.fetch(Goods_detail.class,goods_detail.getId());
detail.setName(goods_detail.getName());
/**
* 不管更新一条还是更新一个对象,那些没有传入数据的字段值是不会改变的
*/
//dao.update(detail,"^name$");//只更新一条 正则表达式^name$
dao.update(detail);//或者更新一个对象的数据
}