1 Star 1 Fork 0

xsjiang / light-dao

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

light-dao是一个基于spring的JdbcTemplate模块封装的DAO层辅助框架,主要用于构建中小型项目,解决DAO层对数据库的处理(实体插入,更新,删除,动态条件查询语句构建,数据自动填充)。

Demo

Model

@TableName("t_user")
public class User {
	@Column(primaryKey = true)
	private String id;
	private String username;
	private String password;
	private Integer age;
	private String name;
	private String roleId;
	@IgnoreColumn({IgnoreColumn.UPDATE, IgnoreColumn.INSERT})
	private String roleName;
	...
}

Dao

public class UserDao extend BaseDao<User> {

	@Autowire
	private RoleDao roleDao;

	public UserDao() {
		super(User.class);
	}
	
	public User get(String username, String password) {
		String sql = sql("select * from @tableName where @username = ? and @password = ?").toSql();
		return super.getOne(sql, getRowMapper(), username, password);
	}
	
	//与角色表连接查询,并动态添加name和age条件
	@Override
	public Page<User> query(Page<User> page) {
        	ParamKit p = new ParamKit(page.getParams());
        	SqlBuilder builder =
                	sql("select u.*, $r.name role_name from @tableName u")
			.sql("left join $r on $r.id = u.@roleId")
	                .where("@name like ?", p.has("name"), p.asStr("name", "%", "%"))
			.and("@age > ?", p.has("age"), p.asInt("age"))
			.addBeanInfo("r", roleDao.getBeanInfo());

        	return super.query(page, getRowMapper(), builder, builder.getValueArray());
    	}
}

Service

public class UserService {
	@Autowire
	private UserDao dao;
	
	public void add(User user) {
		dao.insert(user);
	}
	
	public void update(User user) {
		dao.update(user);
	}
	
	public void updatePassword(String id, String pwd) {
		dao.update(id, "password", pwd);//便捷的属性更新方法
	}
}

SQL构建工具

你可以使用SqlBuilder来辅助你构建sql语句,它能帮你解决数据库表结构的硬编码和条件组装查询问题。

基本用法

String sql = new SqlBuilder().sql("select * from t_user").toSql();

这看起来没什么意义,如果你想构建这么个sql那么你确实没必要使用SqlBuilder

变量

如果你不想在sql语句中硬编码表名或字段等信息,你可以像下面这样使用。

Map<String, String> map = new HashMap<>();
map.put("username", "f_username");
map.put("password", "f_password");
String sql = new SqlBuilder(map)
	.addVar("tableName", "t_user")
	.sql("select * from @tableName where @username=? and @password=?")
	.toSql();

在sql语句中凡是使用@符号标注的都会被替换,你可以在创建SqlBuilder时提供一个map,key表示变量名,value表示被替换的值,你也可以通过addVar(String, String)方法增加变量。

“那map的值我还是硬编码了呀!”。通常实体对应的表字段和表名已经提供了工具类获取了,在下面的章节我们会详细讲这部分内容,目前你只需要知道怎么使用变量即可。

分组变量

分组变量是为了解决有同名变量冲突的问题,为什么会有同名变量呢?通常我们在做连表查询的时候,会用到其他DAO的变量(从BaseDao中我们可以得到一个表结构的map映射),所以当两个表有同名字段的时候也就意味着有同名的变量。

//Deprecated
Map<String, String> map1 = new HashMap<>();
map1.put("tableName", "t_product");
map1.put("categoryId", "category_id");
map1.put("name", "name");

Map<String, String> map2 = new HashMap<>();
map2.put("tableName", "t_product_category");
map2.put("name", "name");
map2.put("id", "id");

new SqlBuilder()
	.addVar(map1)//默认的变量集合
	.addVar("a", map2)//分组a的变量集合
	.sql("select p.*, c.@a!name category_name from @tableName p left join @a!tableName c on p.@categoryId = p.@a!id")
	.toSql();

可以发现有些变量中有一个"!"感叹号,这个感叹号的左边就是分组的key,右边是变量集合的变量名,所以带有"a!"的变量都会从map2中查找,而不会从map1中查找。

连表查询

上面的使用方式(分组变量)已经放弃,从1.4版本开始使用下面的方式,简洁了其他表属性的引用。

new SqlBuilder()
	.addBeanInfo("c", categoryDao.getBeanInfo())
	.autoAppendTableAlias(true)
	.sql("select p.*, $c.name category_name from @tableName p left join $c on $c.id = p.@categoryId");

$符号和.符号之间的字符表示实体变量,.符号后面的表示属性名(非字段名),如果你单单使用$c则表示表名。 autoAppendTableAlias方法会自动为$产生表别名,这可以避免与其他表有同名属性时产生的sql错误。

条件查询

Map<String, String> map = ...;
map.put("age", 18);
ParamKit p = new ParamKit(map);
SqlBuilder sb = new SqlBuilder(map)
	.sql("select * from @tableName")
	.where("@age > ?", p.has("age"), p.asInt("age"))
	.and("@sex = ?", p.has("sex"), p.asStr("sex"));
String sql = sb.toSql();
Object[] params = sql.getValueArray();
//select * from t_user where age > ?
//params只有一个age参数值。

以上代码帮你解决了if语句和处理sql的拼接等麻烦的问题。

命名式参数

SqlBuilder sb = new SqlBuilder(map)
	.sql("select * from @tableName")
	.where("@age > :age", age != null, age)
	.and("@sex = :sex", "m".equals(sex) || "f".equals(sex), sex);
Map<String, Object> params = sb.getValueMap();
//使用namedJdbc来处理

表名

new SqlBuilder().addVar("tableName", "t_user").sql("select * from @tableName");

new SqlBuilder().sql("select * from").table("t_user");

new SqlBuilder().sql("select * from").table(User.class);

第三种的方式你的User必须要添加一个@TableName注解。

排序

SqlBuilder sb = ...;
sb.order("@age", "-@birthday");

上面的排序的意思是按照年龄升序然后降序出生日期,未带"-"号的表示升序,带有"-"号的表示降序。

分页查询

SqlBuilder目前只支持mysql的分页查询。

String sql = new SqlBuilder().sql(...).toSql(10, 20);
//select * from (select * from xxx) limit 10, 20

DAO

你只需要继承BaseDao就可以为你实现基础的CRUD,在BaseDao中使用了@Autowire注入一个javax.sql.DataSource,所以在你的应用中需要有一个数据源实现。 注:从2.x版本开始,要求你的应用有一个JdbcTemplate的bean,而不是javax.sql.DataSource实现了。

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        jdbc = jdbcTemplate;
        ...
    }
public class UserDao extends BaseDao<User> {
  public UserDao() {
    super(User.class);
  }
  
  @Override
  public Page<User> query(Page<User> page) {
    SqlBuilder sqlBuilder = sql("select * from @tableName");
    return super.query(page, getRowMapper(), sqlBuilder);
  }
}

你必须提供一个beanclass给父类,这样BaseDao才能知道怎么处理这个Bean的信息。你需要实现自己的查询方法提供给业务层,因为很少情况会出现无条件查询,所以BaseDao不给出这样的一个query方法,但提供了很多便捷的查询方法。

查询方法都需要你提供一个RowMapper,这样BaseDao才能将行记录转换成实体,你可以从BaseDao#getRowMapper()获取到一个默认的mapper,这个mapper会简单的填充行记录到实体上,空值会忽略,你可以提供你自己的mapper。比如你做了连表查询,多出了一个额外字段。

RowMapper<User> mapper = super.createRowMapper(new BeanSetter<User>() {
  @Override
  public void bean(User bean, ResultSet rs, int rowNum) {
    //此时的bean已经设置了基本的属性
    //设置额外的属性
  }
});
super.query(sql, mapper);

在DAO中使用SqlBuilder

当在DAO中写sql时,我建议你使用sql()来获取一个sql构建器,这个构建器已经具备了实体的属性变量和表名变量。

SqlBuilder sb = sql("select * from @tableName where @username = ? and @password = ?");

上面的代码中,tableName是固定为实体的表名变量,其他则跟你在实体中定义的属性有关。

从2.x开始,提供了一个简化的sql方法,并设置了.autoAppendTableAlias(true)

在DAO中使用SqlBuilder做连表查询

SqlBuilder builder = sql("select p.*, $c.name category_name from @tableName p left join $c on $c.id = p.@categoryId")
	.addBeanInfo("c", categoryDao.getBeanInfo());

使用其他DAO获取属性到字段的映射作为变量添加到sql构建器中。

ID生成

BaseDao提供了自增id和uuid的实现,你只需要在实体的主键上加上@Column(idGenerator = IdGenerator.AUTO_INCREMENT)就可以实现id自增,前提是你的主键是个整数类型,在调用BaseDao#insert(Object)方法时,会根据ID生成器类型生成id值,不过目前也就支持自增和uuid而已,第三种就是你自己分配值。

实体(Entity)

在使用dao之前我们得先为实体和表做映射,我们的映射方式是在实体属性上标注注解。

@TableName("t_user")
public class User {
  @Column(primaryKey=true)
  private String id;
  private String username;
  @Column("pwd")
  private String password;
  @Column(columnType = java.sql.Date.class)
  private Date birthday;
  @IgnoreColumn
  private String leaderName;
  //getter and setter
}

如果你的属性和字段名称一样,那么可以不用为属性添加@Column注解,没有@Column注解的属性都按照驼峰下划线分割方式来做对应关系,如:userName(属性) -> user_name(字段)。

每个实体都应该有一个业务无关的主键,你需要明确给某个属性标识为“主键”,使用@Column(primaryKey = true)来将某个属性标识为主键。

如果你想忽略某个属性的映射关系,那么可以使用@IgnoreColumn标注,这样BaseDao在做CRU操作时会忽略这个属性,其实在应用启动后BaseDao就会将bean的信息缓存在map中,但不会缓存被@IgnoreColumn的属性。

在查询的时候,BaseDao会帮你将行数据填充到实体,从ResultSet获取值的时候使用的是getObject(String)方法,但某些时候无法获取到正确的类型,这使用你需要在注解里指定字段的类型。

@Column(columnType = java.sql.Timestamp.class)
private Date regTime;

空文件

简介

暂无描述 展开 收起
Java
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/xsjiang/light-dao.git
git@gitee.com:xsjiang/light-dao.git
xsjiang
light-dao
light-dao
master

搜索帮助