Mysql4J MySQL ORM 组件开源项目

我要开发同款
匿名用户2020年03月03日
102阅读
开发技术Java
所属分类程序开发、ORM/持久层框架
授权协议MIT

作品详情

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非常简单,几乎不用阅读文档,仅根据javaIDE的代码工具提示,就可以构建完整的程序代码。

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

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

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

//表(mapping类)um_memberm=newum_member();//查询对象Queryquery=newQuery();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(newDate(),DATETIME));query.order(m.reg_time.desc());query.limit(0,20);//获得结果集Listlist=query.getList();其中um_member为数据库表,类名直接对应表名,也是Mysql4J的mapping类,用来配置表结构。um_memberm=newum_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类,它不仅说明了映射关系,还能被实例化成一个对象,在数据操作中使用。

具体使用方法如下:

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

//会员表CREATETABLE`um_member`(   `id`bigint(20)NOTNULLAUTO_INCREMENT,   `name`varchar(20)DEFAULTNULL,   `nick`varchar(30)DEFAULTNULL,   `level`smallint(6)DEFAULTNULL,   `tel`varchar(20)DEFAULTNULL,   `reg_time`datetimeDEFAULTNULL,   `last_login_time`datetimeDEFAULTNULL,   `login_times`int(11)DEFAULTNULL,   PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8它的配置类则按照如下方式书写:

//类名=表名:类名需要同数据库表名保持完全一致,该类必须继承Table作为父类publicclassum_memberextendsTable{//构造方法的固定写法publicum_member(){//数据映射对象的class,可以为Row.class或者pojo对象的class,这里采用pojo对象super(Member.class);//初始化,固定写法,不可省略super.init();//获得可用的JdbcTemplate对象JdbcTemplatejdbcTemplate=(JdbcTemplate)YourSpring.get("jdbcTemplate");//设置jdbcTemplatesuper.setJdbcTemplate(jdbcTemplate);}//属性名=字段名:属性名需要同数据库字段名保持完全一致publicNumid=newNum().asPrimaryKey().asIncrement();//主键IDpublicChrname;//姓名publicChrnick;//昵称publicNumlevel;//会员等级publicChrtel;//电话publicDatetimereg_time=newDatetime().property("registerTime");//注册时间publicDatetimelast_login_time;//最近登录时间publicNumlogin_times;//登录次数}该类继承了cn.com.ursaminor.mysql4j.core.Table类,Table类是所有配置类的父类,配置类必须继承此类,继承该类不需要实现任何抽象方法。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

编写pojo类

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

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

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

super(Member.class);Member类源码如下:

//会员对象publicclassMember{privateLongid;//主键IDprivateStringname;//姓名privateStringnick;    //昵称privateIntegerlevel;//会员等级privateStringtel;//电话privateDateregisterTime;//注册时间privateDatelastLoginTime;//最近登录时间privateIntegerloginTimes;//登录次数publicLonggetId() {returnid;}publicvoidsetId(Longid) {this.id=id;}//省略其它属性的getter/setter方法}使用Row

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

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

使用Query查询数据

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

A:例1

//值intlevel=2;Stringname="ur";Stringnick="小熊";Datedate=newDate(System.currentTimeMillis()-7*24*60*60*1000);//表um_memberm=newum_member();//构建sqlQueryquery=newQuery();query.from(m);                             //fromum_member mquery.where(m.level.eq(level));               //m.level=2query.where(m.name.startsWith(name));        //m.namelike'ur%'query.where(m.nick.contains(nick))            //m.nicklike'%小熊%' .nullIgnore();             //如果String变量nick为空,则忽略m.nicklike...query.where(m.reg_time.ge(date,DATETIME));   //m.reg_time>='2020-01-0217:42:09'query.order(m.reg_time.desc());               //orderbym.reg_time descquery.limit(0,20);                           //limit0,20//执行List<Member>list=query.getList(Member.class);上述语句生成sql为:

selectt0.id,t0.name,t0.nick,t0.level,t0.tel,t0.reg_time,t0.last_login_time,t0.login_timesfromum_membert0wheret0.level=2andt0.namelike'ur%'andt0.nicklike'%小熊%'andt0.reg_time>='2020-01-0217:42:09'orderbyt0.reg_timedesclimit0,20表别名实际为t0,同变量名m不一致,不会导致任何问题。下同。

B:例2

//值intlevel=2;Datedate=newDate(System.currentTimeMillis()-7*24*60*60*1000);//表um_memberm=newum_member();um_order_formof=newum_order_form();//query支持链式方法调用Queryquery=newQuery()query.from(m)                            //fromum_member m .from(of,m.id.eq(of.m_id))             //,um_order_form ofwherem.id=of.m_id.select(m.name)                       //selectm.name.select(Fun.sum(of.count).as("count"))  //sum(of.count)ascount.where(m.level.eq(level))               //m.level=2.where(m.reg_time.ge(date,DATETIME))   //m.reg_time>='2020-01-0217:42:09'.having(Fun.sum(of.count).ge(5))        //havingsum(of.count)>=5.group(m.name);                      //groupbym.name//执行List<Member>list= query.getList(Member.class);上述语句生成sql为:

selectt2.name,sum(t3.count)ascountfromum_membert2,um_order_formt3wheret2.id=t3.m_idandt2.level=2andt2.reg_time>='2020-01-0217:42:09'groupbyt2.namehavingsum(t3.count)>=5C:例3

//表um_memberm=newum_member();um_order_formof=newum_order_form();//子查询Querysub=newQuery().from(of)                        //fromum_order_form of.select(of.m_id)                   //selectof.m_id.having(Fun.sum(of.count).ge(5))   //havingsum(of.count)>=5.group(of.m_id);                   //groupbyof.m_id//构建sqlQueryquery=newQuery();query.from(m);                       //fromum_membermquery.where(m.id.in(sub));           //wherem.idin(...)//执行List<Member>list= query.getList(Member.class);上述语句生成sql为:

selectt5.id,t5.name,t5.nick,t5.level,t5.tel,t5.reg_time,t5.last_login_time,t5.login_timesfromum_membert5wheret5.idin(selectt6.m_idfromum_order_formt6groupbyt6.m_idhavingsum(t6.count)>=5)得到特定结构的查询结果

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

方法说明List<R>getList()返回以List形式表示的结果集,元素类型<R>为配置类中指定的class,List的元素可以为pojo对象或者Row对象IntegergetCount()返回忽略分页后的结果行数,用于分页查询中VgetValue(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关键字指定且仅指定一列。RgetOne()返回一条记录,数据类型<R>为配置类中指定的class,List的元素可以为pojo对象或者Row对象。当没有匹配结果时,返回null。Map<Object,R>getMap(Stringproperty)返回以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_memberm=newum_member();//值Stringname="ursaminor";Stringnick="小熊";Integerlevel=1;Stringtel="1234567890";DateregisterTime=newDate();DatelastLoginTime=null;IntegerloginTimes=0;//构建sqlInserterinserter=newInserter(m);          //insertintoum_memberinserter.set(m.name,name);                //name, //'ursaminor'inserter.set(m.nick,nick);                   //nick,  //'小熊'inserter.set(m.level,level);                 //level, //1inserter.set(m.tel,tel);                     //tel,  //'1234567890'inserter.set(m.reg_time,registerTime);        //reg_time, //'2020-01-0217:42:09'inserter.set(m.last_login_time,lastLoginTime);  //last_login_time,//nullinserter.set(m.login_times,loginTimes);       //login_times, //0//提交inserter.execute();上述语句生成sql为:

insertintoum_member(name,nick,level,tel,reg_time,last_login_time,login_times)values('ursaminor','小熊',1,'1234567890','2020-01-0917:42:09',null,0)B:使用pojo/Row对象创建一个insert语句

//表um_memberm=newum_member();//pojo对象Membermember=newMember();member.setName("ursaminor");member.setNick("小熊");member.setLevel(1);member.setTel("1234567890");member.setRegisterTime(newDate());//提交Inserter.insert(m,member);上述语句生成sql为:

insertintoum_member(name,nick,level,tel,reg_time,last_login_time,login_times)values('ursaminor','小熊',1,'1234567890','2020-01-0917:42:09',null,null)C:Inseter还可以生成insertinto...select...语句

//表um_memberm=newum_member();um_order_formof=newum_order_form();//值intlevel=2;Datedate=newDate(System.currentTimeMillis()-7*24*60*60*1000);//Query对象,用来生成select语句Queryquery=newQuery();query.from(m);                           //fromum_membermquery.select(m.id.as(of.m_id));              //selectm.idasm_idquery.select(Fun.eval("1").as(of.count));       //1ascountquery.select(Fun.eval("8.00").as(of.price));    //8.00aspricequery.where(m.level.ge(level));              //wherem.level>=2query.where(m.reg_time.ge(date,DATETIME)); //m.reg_time>='2020-01-0217:42:09'      //构建sqlInserterinserter=newInserter(of);          //insertintoum_order_forminserter.columns(of.m_id,of.count,of.price);   //(m_id,count,price)inserter.with(query);                    //select...//执行inserter.execute();上述语句生成sql为:

insertintoum_order_form(m_id,count,price)    selectt11.idasm_id,1ascount,8.00aspricefromum_membert11wheret11.level>=2andt11.reg_time>='2020-01-0217:42:09'如果表的主键是自增长的,新增记录后的主键值,会自动设置到pojo类中,该pojo类需要有主键的setter方法。

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

使用Updater修改数据

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

举例:

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

//表um_memberm=newum_member();//要修改的值Longid=2020L;Stringnick="小熊";Integerlevel=1;Stringtel="1234567890";DatelastLoginTime=newDate();IntegerloginTimes=1;//构建sqlUpdaterupdater=newUpdater(m);          //updateum_member mupdater.set(m.nick,nick);                   //setm.nick='小熊'updater.set(m.level,level);                  //m.level=1updater.set(m.tel,tel);                      //m.tel='1234567890'updater.set(m.last_login_time,lastLoginTime);  //m.last_login_time='2020-01-0917:42:09'updater.set(m.login_times,loginTimes);       //m.login_times=1updater.where(m.id.eq(id));            //wherem.id=2020//执行updater.execute();上述语句生成sql为:

updateum_membert14sett14.nick='小熊',t14.level=1,t14.tel='1234567890',t14.last_login_time='2020-01-0917:42:09',t14.login_times=1wheret14.id=2020B:使用pojo/Row对象创建一个update语句

//表um_memberm=newum_member();//pojo类Membermember=newMember();member.setId(2020L);member.setName("ursaminor");member.setNick("小熊");member.setLevel(1);//执行Updater.update(m,member);上述语句生成sql为:

updateum_membersetname='ursaminor',nick='小熊',level=1,tel=null,reg_time=null,last_login_time=null,login_times=nullwhereid=2020C:Updater还可以生成带有select子句的update语句

//表um_memberm=newum_member();um_order_formof=newum_order_form();//临时表classTempTableextendsQuery{publicNumid;}//临时表就是一个Query对象TempTablett=newTempTable();tt.from(of);                      //fromum_order_form oftt.select(of.m_id.as(tt.id));          //selectof.m_idasidtt.group(of.m_id);                 //groupbyof.m_idtt.having(Fun.count(of.id).ge(10));    //havingcount(of.id)>=10//构建sqlUpdaterupdater=newUpdater(m);    //updateum_member mupdater.with(tt,tt.id.eq(m.id));      //(select...)tt wherett.id=m.idupdater.set(m.level,2);               //setm.level=2//执行updater.execute();上述语句生成sql为:

updateum_membert16,(selectt17.m_idasidfromum_order_formt17groupbyt17.m_idhavingcount(t17.id)>=10)t18sett16.level=2wheret18.id=t16.id使用Deleter删除数据

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

举例:

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

//表um_memberm=newum_member();//值Integerlevel=1;//构建sqlDeleterdeleter=newDeleter(m);        //deletemfromum_membermdeleter.where(m.level.eq(level));         //wherem.level=1//提交deleter.execute();上述语句生成sql为:

deletet19fromum_membert19wheret19.level=1B:使用pojo/Row对象创建一个delete语句

//表um_memberm=newum_member();//pojo对象Membermember=newMember();member.setId(2020L);//提交Deleter.delete(m,member);上述语句生成sql为:

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

//表um_memberm=newum_member();um_order_formof=newum_order_form();//临时表classTmextendsQuery{publicNumid;}//临时表就是一个Query对象Tms=newTm();s.from(of);                         //fromum_order_formofs.select(of.m_id.as(s.id));             //selectof.m_idasids.group(of.m_id);                   //groupbyof.m_ids.having(Fun.count(of.id).ge(10));      //havingcount(of.id)>=10//构建sqlDeleterdeleter=newDeleter(m);      //deletemfromum_membermdeleter.with(s,s.id.eq(m.id));         //(select...)swheres.id=m.id//执行deleter.execute();上述语句生成sql为:

deletet21fromum_membert21,(selectt22.m_idasidfromum_order_formt22groupbyt22.m_idhavingcount(t22.id)>=10)t23wheret23.id=t21.id
声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论