首页 资讯频道 互联频道 智能频道 网络 数据频道 安全频道 服务器频道 存储频道

Mysql4J:可访问MySQL数据库的ORM组件

2020-03-06 10:05:04 来源 : 开源中国

Mysql4J是一款用于访问MySQL数据库的ORM组件。通过它可以方便地构建sql语句,并且可在编译期进行sql语法检查,还可以把得到的数据结果转换成List、Map、Set等形式。简单易用,独辟蹊径,是提高代码编写效率的好工具。

Mysql4J适用于中小型项目。

特点

底层基于Spring的JdbcTemplate

Mysql4J底层通过Spring的JdbcTemplate对象访问数据库,该JdbcTemplate对象需要事先配置好并保证可用,然后设置到Mysql4J中。

编译期纠错

Mysql4J可在编译期检查出大部分的sql语法错误,相比于在运行时才发现sql错误,对于快速开发,有很大帮助。

由于用java模拟了sql语句,所以sql的语法,对应到了java语法,这样当sql拼写不正确时(逻辑错误除外),通常也是一个java语法错误,如果使用IDE的代码提示工具,则基本的匹配和代码构建是自动提示的。

数据结果,封装友好

Mysql4J对查询得到的数据,可进行相对灵活的后处理。可以是pojo对象的List集合,也可以是Map集合。同JdbcTemplate不同,JdbcTemplate的getMap方法,仅返回Map表示的一条数据记录,Mysql4J则把多行查询结果,放在Map中,Map的key值,可以由程序员指定,通常指定成表中某个不重复的字段。还可以方便的直接得到单个值,或者某一列值的List列表。

如果不想定义pojo对象,可以直接使用Row对象封装行记录,Row实现了Map接口,提供了getString、getInteger、getDate等方便的数据提取方法。

Api 简单易懂,上手迅速

Mysql4J的api非常简单,几乎不用阅读文档,仅根据java IDE的代码工具提示,就可以构建完整的程序代码。

SQL 即用即写,逻辑集中不分散

sql语句中,通常包含着关键的业务逻辑。Mysql4J可保证在一个java方法中,业务逻辑的完整性,sql语句不会被写到程序文件之外。打开一个类,逻辑全部在其内部展现,相对于把sql写到xml中,代码更清晰易读。

使用

Mysql4J的使用非常简单,例如:

// 表(mapping类)

um_member m = new um_member();

// 查询对象

Query query = new Query();

query.from(m);

query.where(m.level.eq(1));

query.where(m.name.startsWith("urs"));

query.where(m.nick.contains("小熊"));

query.where(m.reg_time.le(new Date(), DATETIME));

query.order(m.reg_time.desc());

query.limit(0, 20);

// 获得结果集

List list = query.getList();

其中um_member为数据库表,类名直接对应表名,也是Mysql4J的mapping类,用来配置表结构。um_member m = new um_member(),变量m,可理解为表的别名,m.level,m.name,m.nick,m.reg_time是m对象的属性名,同时也是表um_member的字段名!

这样映射后,数据库中表名和字段名的改变,只需要修改mapping类um_member,然后分布在程序各处的Mysql4J数据库访问代码,会自动报出编译期错误,方便修改,保证了表名和字段名的一致。类似报出编译期错误警告的地方还有很多,比如把数字赋值给一个字符字段,等等。

Mysql4J不仅把表和字段直接映射成java的类和属性,而且把查询、插入、修改、删除数据的操作,也做了对象化处理。这些都采用java实现并且尽量遵循sql的语法习惯!

编写mapping类

使用Mysql4J,需要配置一个mapping类。在传统的ORM组件中,这个配置通常以xml格式写在文件里,用来指明java数据模型和数据库中的表以及字段的映射关系。在Mysql4J中,这个配置就是一个java类,它不仅说明了映射关系,还能被实例化成一个对象,在数据操作中使用。

具体使用方法如下:

首先假设数据库中存在如下的表:

// 会员表

CREATE TABLE `um_member` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`name` varchar(20) DEFAULT NULL,

`nick` varchar(30) DEFAULT NULL,

`level` smallint(6) DEFAULT NULL,

`tel` varchar(20) DEFAULT NULL,

`reg_time` datetime DEFAULT NULL,

`last_login_time` datetime DEFAULT NULL,

`login_times` int(11) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

它的配置类则按照如下方式书写:

// 类名 = 表名:类名需要同数据库表名保持完全一致,该类必须继承Table作为父类

public class um_member extends Table

{

// 构造方法的固定写法

public um_member()

{

// 数据映射对象的class,可以为Row.class或者pojo对象的class,这里采用pojo对象

super(Member.class);

// 初始化,固定写法,不可省略

super.init();

// 获得可用的JdbcTemplate对象

JdbcTemplate jdbcTemplate = (JdbcTemplate)YourSpring.get("jdbcTemplate");

// 设置jdbcTemplate

super.setJdbcTemplate(jdbcTemplate );

}

// 属性名 = 字段名:属性名需要同数据库字段名保持完全一致

public Num id = new Num().asPrimaryKey().asIncrement(); // 主键ID

public Chr name; // 姓名

public Chr nick; // 昵称

public Num level; // 会员等级

public Chr tel; // 电话

public Datetime reg_time = new Datetime().property("registerTime"); //注册时间

public Datetime last_login_time; // 最近登录时间

public Num login_times; // 登录次数

}

该类继承了cn.com.ursaminor.mysql4j.core.Table类,Table类是所有配置类的父类,配置类必须继承此类,继承该类不需要实现任何抽象方法。

配置类的类名,必须同表名相同,这可能违反了java的命名规范,但使得配置类在使用时,更像一个表,而不是一个对象。

配置类由两部分组成,一个是构造方法,一个是成员变量,不需要普通的方法。

构造方法对参数无要求,内部要求必须调用下列父类的方法:

一个是父类构造方法super(Class cls),用来指定数据查询结果的装载对象,即封装数据的pojo对象,如果没有pojo对象,可以直接使用Row,即super(Row.class)。

另一个是super.init(),该方法负责初始化工作,必须调用,不可省略。

第三个是super.setJdbcTemplate(jdbcTemplate),jdbcTemplate对象需要使用者自行获取,并保证可用。

后两个方法可以合并成super.init(jdbcTemplate)。

把jdbcTemplate绑定到配置类,可以自然地支持多数据库情况。并且在使用时,无须再设置jdbcTemplate,只需要简单实例化配置类即可:

um_member m = new um_member();

配置类中,需要配置同表字段一一对应的成员变量,成员变量的名称,必须同表的字段名保持一致,如:

public Num login_times;

这里也可能会违反java的命名规范,但这使配置类应用起来更像一个表。类名和属性名如果不同数据库的表名字段名保持一致,将导致sql拼写错误。

成员变量必须被声明为public的,并且只能是Chr、Num、Datetime三种类型之一。Chr代表该属性对应数据库的字符型字段,Num代表该属性对应数据库的数字型字段,Datetime目前只针对datetime类型字段,其它字段类型,会在后续版本扩展。

如果字段不需要其它的修饰,则不需要被赋值,仅保持声明即可,super.init()方法会自动给它们赋值一个对应的对象,不会出现属性为null的情况。如果需要其它修饰,比如设置为主键、设置为自增长、设置pojo类中的对应属性,则需要手动配置。如:

public Num id = new Num().asPrimaryKey().asIncrement();

public Datetime reg_time = new Datetime().property("registerTime");

这里,id属性被设置成主键,并且是自增长类型。reg_time属性被指定了pojo类中对应的属性为registerTime。如果pojo类中对应的属性为regTime,则不需要上述语句,super.init()会自动转化并为其赋值。

配置类可重复使用,虽然配置类是有状态的,但在使用过程中,其状态不会发生改变,可以构建一个配置类的工厂类,也可将配置类作为单例模式写到Spring中。最简单的使用方式,直接new一个就好:um_member m = new um_member();

编写pojo类

pojo类是一个普通的装载数据的对象,遵循JavaBean风格,不需要继承和实现任何父类和接口。

如果表的主键是自增长的,新增记录后的主键值,会自动设置到pojo类中,该pojo类需要有主键的setter方法。

一个表如果有对应的pojo类,则需要在配置类构造方法的super(Class cls)中设置,比如

super(Member.class);

Member类源码如下:

// 会员对象

public class Member

{

private Long id; // 主键ID

private String name; // 姓名

private String nick; // 昵称

private Integer level; // 会员等级

private String tel; // 电话

private Date registerTime; // 注册时间

private Date lastLoginTime; // 最近登录时间

private Integer loginTimes; // 登录次数

public Long getId(){

return id;

}

public void setId(Long id){

this.id = id;

}

// 省略其它属性的getter/setter方法

}

使用Row

如果不使用自定义的pojo类装载数据,Mysql4j提供了Row类来装载数据。Row实现了Map接口,提供了getString、getInteger、getDate等方便的数据提取方法。可以作为Map的替代。

如果表的主键是自增长的,新增记录后的主键值,会自动设置到Row类中。

使用Query查询数据

Query类是Mysql4J的查询类,通过它可以方便的构建大多数的select语句。并且方便地得到想要的数据结果。

A:例1

// 值

int level = 2;

String name = "ur";

String nick = "小熊";

Date date = new Date(System.currentTimeMillis()-7*24*60*60*1000);

// 表

um_member m = new um_member();

// 构建sql

Query query = new Query();

query.from(m); // from um_memberm

query.where(m.level.eq(level));// m.level=2

query.where(m.name.startsWith(name)); // m.name like 'ur%'

query.where(m.nick.contains(nick))// m.nick like '%小熊%'

.nullIgnore(); // 如果String变量nick为空,则忽略m.nick like...

query.where(m.reg_time.ge(date, DATETIME));// m.reg_time>='2020-01-02 17:42:09'

query.order(m.reg_time.desc());// order by m.reg_timedesc

query.limit(0, 20);// limit 0,20

// 执行

List list = query.getList(Member.class);

上述语句生成sql为:

select t0.id,t0.name,t0.nick,t0.level,t0.tel,t0.reg_time,t0.last_login_time,t0.login_times

from um_member t0

where t0.level=2 and t0.name like 'ur%' and t0.nick like '%小熊%'

and t0.reg_time>='2020-01-02 17:42:09'

order by t0.reg_time desc limit 0,20

表别名实际为t0,同变量名m不一致,不会导致任何问题。下同。

B:例2

// 值

int level = 2;

Date date = new Date(System.currentTimeMillis()-7*24*60*60*1000);

// 表

um_member m = new um_member();

um_order_form of = new um_order_form();

// query支持链式方法调用

Query query = new Query()

query.from(m) // from um_memberm

.from(of, m.id.eq(of.m_id))// , um_order_formof where m.id=of.m_id

.select(m.name) // select m.name

.select(Fun.sum(of.count).as("count"))// sum(of.count) as count

.where(m.level.eq(level))// m.level=2

.where(m.reg_time.ge(date, DATETIME))// m.reg_time>='2020-01-02 17:42:09'

.having(Fun.sum(of.count).ge(5))// having sum(of.count)>=5

.group(m.name); // group by m.name

// 执行

List list = query.getList(Member.class);

上述语句生成sql为:

select t2.name,sum(t3.count) as count

from um_member t2,um_order_form t3

where t2.id=t3.m_id and t2.level=2 and t2.reg_time>='2020-01-02 17:42:09'

group by t2.name having sum(t3.count)>=5

C:例3

// 表

um_member m = new um_member();

um_order_form of = new um_order_form();

// 子查询

Query sub = new Query()

.from(of) // from um_order_formof

.select(of.m_id)// select of.m_id

.having(Fun.sum(of.count).ge(5))// having sum(of.count)>=5

.group(of.m_id);// group by of.m_id

// 构建sql

Query query = new Query();

query.from(m);// from um_member m

query.where(m.id.in(sub));// where m.id in ( ... )

// 执行

List list = query.getList(Member.class);

上述语句生成sql为:

select t5.id,t5.name,t5.nick,t5.level,t5.tel,t5.reg_time,t5.last_login_time,t5.login_times

from um_member t5 where t5.id in

(select t6.m_id from um_order_form t6 group by t6.m_id having sum(t6.count)>=5)

得到特定结构的查询结果

Query对象返回的数据结果,可以有多种形式。

方法 说明
List<R> getList() 返回以List形式表示的结果集,元素类型<R>为配置类中指定的class,List的元素可以为pojo对象或者Row对象
Integer getCount() 返回忽略分页后的结果行数,用于分页查询中
V getValue(Class<V> valueClass) 返回一个值,值的类型由valueClass指定,比如String.class、Integer.class。当数据结果有多条记录时,仅返回第一条记录;当没有匹配结果时,返回null。该方法要求select关键字指定且仅指定一列。
List<V> getValues(Class<V> valueClass) 返回值的List,元素类型由valueClass指定,比如String.class、Integer.class。当没有匹配结果时,返回空的List对象。该方法要求select关键字指定且仅指定一列。
Set<V> getSet(Class<V> valueClass) 返回值的Set,元素类型由valueClass指定,比如String.class、Integer.class。当没有匹配结果时,返回空的Set对象。该方法要求select关键字指定且仅指定一列。
R getOne() 返回一条记录,数据类型<R>为配置类中指定的class,List的元素可以为pojo对象或者Row对象。当没有匹配结果时,返回null。
Map<Object,R> getMap(String property) 返回以Map形式表示的结果集,需要指定key,即pojo的property。Value的类型<R>为配置类中指定的class。value可以为pojo对象或者Row对象。当没有匹配结果时,返回空的Map对象。

Map<K,R> getMap(KeyCallback<K,R> keyCallback)

返回以Map形式表示的结果集,通过回调获得属性值作为key。Value的类型<R>为配置类中指定的class。value可以为pojo对象或者Row对象。当没有匹配结果时,返回空的Map对象。

Map<K,V> getMap(KeyValueCallback<K,V,R> keyValueCallback)

返回以Map形式表示的结果集。<R>指定了最初返回数据的类型,为配置类中指定的class。该方法通过回调获得属性值作为key和value。当没有匹配结果时,返回空的Map对象。

使用Inserter插入数据

如果要在表中插入新数据,可使用Inserter类,Inserter类用来构建insert语句并提交。

举例:

A:使用值创建一个insert语句

// 表

um_member m = new um_member();

// 值

String name = "ursaminor";

String nick = "小熊";

Integer level = 1;

String tel = "1234567890";

Date registerTime = new Date();

Date lastLoginTime = null;

Integer loginTimes = 0;

// 构建sql

Inserter inserter = new Inserter(m); // insert into um_member

inserter.set(m.name, name); // name, // 'ursaminor'

inserter.set(m.nick, nick); // nick, // '小熊'

inserter.set(m.level, level); // level, // 1

inserter.set(m.tel, tel); // tel, // '1234567890'

inserter.set(m.reg_time, registerTime); // reg_time, // '2020-01-02 17:42:09'

inserter.set(m.last_login_time, lastLoginTime);// last_login_time, // null

inserter.set(m.login_times, loginTimes); // login_times, // 0

// 提交

inserter.execute();

上述语句生成sql为:

insert into um_member (name,nick,level,tel,reg_time,last_login_time,login_times)

values ('ursaminor','小熊',1,'1234567890','2020-01-09 17:42:09',null,0)

B:使用pojo/Row对象创建一个insert语句

// 表

um_member m = new um_member();

// pojo对象

Member member = new Member();

member.setName("ursaminor");

member.setNick("小熊");

member.setLevel(1);

member.setTel("1234567890");

member.setRegisterTime(new Date());

// 提交

Inserter.insert(m, member);

上述语句生成sql为:

insert into um_member (name,nick,level,tel,reg_time,last_login_time,login_times)

values ('ursaminor','小熊',1,'1234567890','2020-01-09 17:42:09',null,null)

C:Inseter还可以生成 insert into...select...语句

// 表

um_member m = new um_member();

um_order_form of = new um_order_form();

// 值

int level = 2;

Date date = new Date(System.currentTimeMillis()-7*24*60*60*1000);

// Query对象,用来生成select语句

Query query = new Query();

query.from(m); // from um_member m

query.select(m.id.as(of.m_id)); // select m.id as m_id

query.select(Fun.eval("1").as(of.count));// 1 as count

query.select(Fun.eval("8.00").as(of.price));// 8.00 as price

query.where(m.level.ge(level)); // where m.level>=2

query.where(m.reg_time.ge(date, DATETIME)); // m.reg_time>='2020-01-02 17:42:09'

// 构建sql

Inserter inserter = new Inserter(of); // insert into um_order_form

inserter.columns(of.m_id, of.count, of.price);// (m_id, count, price)

inserter.with(query); // select ...

// 执行

inserter.execute();

上述语句生成sql为:

insert into um_order_form (m_id,count,price)

select t11.id as m_id,1 as count,8.00 as price from um_member t11

where t11.level>=2 and t11.reg_time>='2020-01-02 17:42:09'

如果表的主键是自增长的,新增记录后的主键值,会自动设置到pojo类中,该pojo类需要有主键的setter方法。

如果没有使用pojo类,而是使用Row类型,则主键值也会自动设置。

使用Updater修改数据

如果要修改表中的数据,可使用Updater类,Updater类用来构建update语句并提交。

举例:

A:使用值创建一个update语句

// 表

um_member m = new um_member();

// 要修改的值

Long id = 2020L;

String nick = "小熊";

Integer level = 1;

String tel = "1234567890";

Date lastLoginTime = new Date();

Integer loginTimes = 1;

// 构建sql

Updater updater = new Updater(m); // update um_memberm

updater.set(m.nick, nick); // set m.nick='小熊'

updater.set(m.level, level); // m.level=1

updater.set(m.tel, tel); // m.tel='1234567890'

updater.set(m.last_login_time,lastLoginTime);// m.last_login_time='2020-01-09 17:42:09'

updater.set(m.login_times, loginTimes); // m.login_times=1

updater.where(m.id.eq(id)); // where m.id=2020

// 执行

updater.execute();

上述语句生成sql为:

update um_member t14

set t14.nick='小熊',t14.level=1,t14.tel='1234567890',

t14.last_login_time='2020-01-09 17:42:09',t14.login_times=1

where t14.id=2020

B:使用pojo/Row对象创建一个update语句

// 表

um_member m = new um_member();

// pojo类

Member member = new Member();

member.setId(2020L);

member.setName("ursaminor");

member.setNick("小熊");

member.setLevel(1);

// 执行

Updater.update(m, member);

上述语句生成sql为:

update um_member

set name='ursaminor',nick='小熊',level=1,tel=null,reg_time=null,last_login_time=null,login_times=null

where id=2020

C:Updater还可以生成带有select子句的update语句

// 表

um_member m = new um_member();

um_order_form of = new um_order_form();

// 临时表

class TempTable extends Query

{

public Num id;

}

// 临时表就是一个Query对象

TempTable tt = new TempTable();

tt.from(of); // from um_order_formof

tt.select(of.m_id.as(tt.id));// select of.m_id as id

tt.group(of.m_id); // group by of.m_id

tt.having(Fun.count(of.id).ge(10));// having count(of.id)>=10

// 构建sql

Updater updater = new Updater(m); // update um_memberm

updater.with(tt, tt.id.eq(m.id));// (select ... ) tt where tt.id=m.id

updater.set(m.level, 2);// set m.level=2

// 执行

updater.execute();

上述语句生成sql为:

update um_member t16,

(select t17.m_id as id from um_order_form t17 group by t17.m_id having count(t17.id)>=10) t18

set t16.level=2

where t18.id=t16.id

使用Deleter删除数据

如果要删除表中的数据,可使用Deteler类,Deleter类用来构建delete语句并提交。

举例:

A:使用值创建一个delete语句

// 表

um_member m = new um_member();

// 值

Integer level = 1;

// 构建sql

Deleter deleter = new Deleter(m); // delete m from um_member m

deleter.where(m.level.eq(level));// where m.level=1

// 提交

deleter.execute();

上述语句生成sql为:

delete t19 from um_member t19 where t19.level=1

B:使用pojo/Row对象创建一个delete语句

// 表

um_member m = new um_member();

// pojo对象

Member member = new Member();

member.setId(2020L);

// 提交

Deleter.delete(m, member);

上述语句生成sql为:

delete from um_member where id=2020

C:Deleter还可以生成带有select子句的delete语句

// 表

um_member m = new um_member();

um_order_form of = new um_order_form();

// 临时表

class Tm extends Query

{

public Num id;

}

// 临时表就是一个Query对象

Tm s = new Tm();

s.from(of); // from um_order_form of

s.select(of.m_id.as(s.id));// select of.m_id as id

s.group(of.m_id); // group by of.m_id

s.having(Fun.count(of.id).ge(10));// having count(of.id)>=10

// 构建sql

Deleter deleter = new Deleter(m); // delete m from um_member m

deleter.with(s, s.id.eq(m.id));// (select ... ) s where s.id=m.id

// 执行

deleter.execute();

上述语句生成sql为:

delete t21

from um_member t21,

(select t22.m_id as id from um_order_form t22 group by t22.m_id having count(t22.id)>=10) t23

where t23.id=t21.id

相关文章

最近更新