# jfire-orm **Repository Path**: xingchensoft/jfire-orm ## Basic Information - **Project Name**: jfire-orm - **Description**: jfire体系下的orm框架 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: dev - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 5 - **Created**: 2015-11-16 - **Last Updated**: 2022-06-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #Jfire-Orm框架 --- [TOC] --- ##框架说明 Jfire-Orm是一个半自动的Orm映射框架,基于方法和Sql语句的映射来进行代码和sql语句的解耦.但是同时也提供快捷的单表对象CURD操作.其中单表curd操作是基于标准sql和主键自增机制的.当然也可以通过修改支持更多. Jfire-Orm并不试图提供大而全的解决方案,而是更希望提供方便于实际编程的解决方式.因为是基于标准sql的,其目标是解决方法最后到sql中的解耦问题. ##使用说明 ###基础设置 在Jfire-Orm框架中,使用SqlSession来代表一个数据库连接,而SqlSession是由SessionFactory产生的.所以框架使用的开始就是初始化SessionFactory. ####SessionFactory SessionFactory的实现类是SessionFactoryImpl.初始话的时候需要提供一个连接池对象.代码如下. ```java DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("centerm"); dataSource.setMaxActive(150); dataSource.setMaxWait(500); try { dataSource.getConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } sessionFactory = new SessionFactoryImpl(dataSource); ``` SessionFactory接口非常简单,如下 ```java public interface SessionFactory { /** * 获得当前线程内的SqlSession * * @return */ public SqlSession getCurrentSession(); /** * 重新打开一个SqlSession * * @return */ public SqlSession openSession(); /** * 移除当前线程的SqlSession * */ public void removeCurrentSession(); /** * 将一个session设置到当前的线程中 * * @param session */ public void setCurrentSession(SqlSession session); } ``` ####SqlSession SqlSession表示的一个数据库连接实例.其中包含了大部分的sql功能.这点观看代码很容易理解. 在SqlSession提供的功能之中,最为主要的就是一个查询结果的映射返回,通过数据库字段名和对象字段名之间的映射来完成查询结果到对象的转换.以及单表CURD的操作.其中单表的CURD操作非常的简便易用,直接将对象作为参数传入就可以完成更新,保存和删除操作.其中更新和保存是通过判断对象的id键值是否有值来完成的.如果没有值就是保存(新增)操作,否则就是更新操作.而查询操作,直接传入简单的id和对象类型即可完成查询. 可以看到单表的CURD是很方便易用的,但是直接的sql查询并不是很方便,仍然是原始的使用方式.而实际上,sql操作的核心并不在Session上,而是在于接口与sql语句的绑定. ```java /** * 代表一个connection链接,提供各种dao操作入口 * * @author eric * */ public interface SqlSession extends AutoCloseable { /** * 将一个对象保存或者更新到数据库。如果对象的id属性有值,就是更新操作,如果没有值就是插入操作 * * @param * * @param entity * @return */ public void save(T entity); /** * 删除对象所对应的表的一条记录 * * @param entityClass 代表数据库表的类对象 * @param pk 主键 * @return */ public boolean delete(Object entity); /** * 根据主键获取一条记录,并且使用该记录创造一个对象 * * @param entityClass 代表数据库表的类对象 * @param pk 主键 * @return 代表该行记录的对象实例 */ public T get(Class entityClass, Object pk); /** * 使用sql语句进行查询。 * 如果resultTypes长度是1,并且内容不是基本类型,则将返回的每一行数据按照给定的类型进行对象实例并且,最终返回的结果应该是List * 的形式。 如果resultTypes的长度是1,并且内容是基本类型,则将返回的每一行的单列数据获得,最终返回的结果是List的形式 * 如果resultTypes的长度大于1,则其内容必须都为基本类型,并且将返回的每一行的数据组装成object[]数组,最终返回的结果类型是List * * * @param resultType * @param sql * @param params * @return */ public List query(Class[] resultTypes, String sql, Object... params); /** * 使用sql语句进行查询,要求返回的结果只能是单行。 如果resultType是事先定义的基本类型之一,要求结果只能是单行单列,并且返回该结果 * 如果不是的话,则可以是单行多列,将结果拼装成一个对象实例 * * @param resultType * @param sql * @param params * @return */ public T query(Class resultType, String sql, Object... params); /** * 根据给定的sql语句以及参数,进行更新类型的sql语句操作 * * @param sql * @param params 需要更新的参数,参数顺序需要与sql语句中的一致 * @return 返回操作的数据库行数 */ public int update(String sql, Object... params); /** * 根据给定的sql,执行批量插入。一次迭代为一行更新的数据 * * @param sql 给定的格式化sql * @param iterator 迭代器,每一次迭代的数据为一行的数据 */ public int[] batchUpdate(String sql, List iterator); /** * 根据给定的接口,返回符合sqlorm规范的接口实现 * * @param entityClass * @return */ public T getMapper(Class entityClass); /** * 关闭session,释放数据库链接 */ public void close(); /** * 启动事务,将该数据库链接设置为非自动提交模式 */ public void beginTransAction(); /** * 依据事务传播策略进行事务提交请求操作(在单一事务传播情况下,内嵌事务的提交只会消耗提交数,不会真的执行提交操作) */ public void commit(); /** * 提交事务到数据库,但不改变当前数据库链接的提交模式 */ public void flush(); /** * 事务回滚 */ public void rollback(); /** * 获取当前session使用的数据库链接 * * @return */ public Connection getConnection(); } ``` ###单表CURD操作 以下省略get和set方法 ```java @TableEntity(name = "user")//通过注解映射来表明该对象所映射的表.注意这个注解,只在单表CURD操作中具备意义. public class User { @Id//表明该字段所对应的数据库字段为主键,在curd操作中,该注解是必须的 @Column(name = "userid")//表明该字段对应的数据库字段的名称,如果属性名和数据库字段名一致,则不需要该注解. private Integer id; @Column(name = "username") private String name; private String password; private Integer age; @IgnoreField//使用该注解表明该字段不是数据库的映射字段 private String birthday; } ``` 在有了这样的一个对象后,就可以通过SqlSession进行单表的curd操作了. ###接口和sql语句绑定 在Jfire-Orm框架中,最为常见和实际的使用是接口方法和sql语句的绑定.也就是通过定义一些接口方法,并且将接口方法和sql语句绑定,这样,程序中调用对应的方法就相当于发出sql语句。请见看下面的简单示例 ```java public interface UserOp{ @query(sql="select * from user where id={id}",paramNames={"id"}) public User query(integer id); } public UserService{ public User find(integer id){ SqlSession session = sessionFactory.getCurrentSession(); //获取接口的实例,这个实例是框架自动生成的,每次获取都是一个新对象,同时该对象内使用了当前的session。所以如果session关闭,该实例不可继续进行操作 Userop userop = session.getMapper(Userop.Class); return userop.query(id); } } ``` 通过上面的代码可以很简单的看明白这个sql语句和方法的Orm是怎么使用的。简单的说步骤如下。 1. 定义一个接口,将接口方法上打上orm注解,主要有```@Query,@Update,@BatchUpdate``` 2. 在程序中获得一个session,使用session的getMapper()方法传入接口的类对象,获取接口实例。 3. 使用接口方法,当调用接口方法的时候就相当于是发出了sql语句 ####数据库表名和字段名的映射 在数据库操作中,会遇到的第一个问题就是数据库表名和字段名与类名和类的属性名之间的映射问题。如果可以使用映射,则可以很大程度上的屏蔽数据库名称的改变。Jfire-Orm支持在sql语句中使用占位符来提示字段的映射。 首先有一个User表和User对象,如下。 ```java @tableentity(name="user") Class User{ @id @column(name="userid") private int id; private string age; } ``` 上述的例子中, 表名user映射到类User,字段userid映射到属性id。那么在sql语句中,可以使用```@```来表示后面紧跟着的是一个映射的类,使用```&```来表示后面紧跟着的是属性。也可以采用```类型.属性名```的形式。下面来个例子。 ``` select &User.id,age from @User 这样的语句会被转换成 select userid,age from user 在只有一个类的情况下,属性名前面的类名可以省略,比如上面的sql可以简化成 select &id,age from @User ``` ####简单查询和更新 ```java @Query(sql = "select username from user where userid={userid}",paramNames = { "userid" }) public String getUserName(Integer userid); @Query(sql = "select * from user where userid={id}", paramNames = { "id" }) public List getUserByidWithName(Integer id); @Update(sql = "insert into user (username,age,password,birthday,userid) values({user.name},{user.age},{user.password},{user.birthday},{user.id})", paramNames = { "user" }) public int insertUser(User user); ``` - **简单查询**:如果确定查询的结果只有单行,则返回的类型可以是对象类型.而在sql语句中,可以使用{}这样的占位符来表明需要注入的数据,该注入的数据的名字和方法入参的名称相同.其中方法入参的名称由属性paramNames数组确定,该数组的内容顺序必须和方法入参一致. - **返回对象**:如果查询的结果是一个多列的数据,那么只需要有和该多列数据所对应的对象,查询结果即可自动转换为对象.并且如果查询的结果是多行的话,返回的结果类型必须是List< T >的形式.同时sql语句中的参数注入也可以使用对象的方式.这点和js的语法很像,可以使用类似a.b这样的方式.可以参考第三个方法 - **更新语句**:更新语句在对象注入和方法入参上和查询是相同的,但是返回结果可以是int也可以是void.如果是int会返回该更新语句产生了更新条数结果 ####批量更新 批量更新用于一个更新语句有不同的参数情况。入参为List类型参数。每一个元素都执行一个更新语句。批量更新由于使用了批量提交,加强了批量动作的性能。 ```java @BatchUpdate(sql = "insert into user (username,age,password,birthday,userid) values({user.name},{user.age},{user.password},{user.birthday},{user.id})", paramNames = { "user" }) public int[] insertUsers(List user); ``` 批量更新可以返回void也可以返回int[].如果是后者表明是每一个更新动作更新的条数.批量更新的方法入参只能为List形的参,并且需要明确指定泛型类型..,参数的个数不限,可以是多个,但是每一个list的size必须是相同的.paramNames的内容仍然是方法的入参名称,而在sql中的注入占位的起头也是入参的名称. ####动态sql #####1.非空支持 在查询之中,很常见一种情况就是很多的查询条件,而且任意为空,这个时候大多数都需要程序员进行sql语句拼接,写出如下类似的代码 ```java String sql = "select * from user where 1=1 " if(age!=null){ sql +=" and age="+age; } if(name!=null){ sql +=" and name="+name; } ``` 如果这样的条件很多,则代码的可读性会变得很差,而且也会为编程带来负担。为了简化这种非空条件动态查询,**Jfire-Orm**支持动态sql语句查询。请看如下代码 ```java @Query(sql = "select * from user where 1=1 [user.age] and age={user.age}# [user.name] and username={user.name}# [user.id] and userid={user.id}#", paramNames = { "user" }) public List dynamicQuery(User user); ``` - **条件判断的开始和结束**:如果一个变量使用[]包围起来表明这个变量需要进行非空判断,如果该变量不为空,则到最近的一个#号之间的内容会被追加到sql语句中。而在语句中的条件占位符注入等与之前的语法是等同的 - **动态sql语句自动判断**:程序会通过扫描sql语句,来判断是否为动态sql。需要注意的是,动态sql其实是在程序内部每次使用一个StringBuilder对象来进行sql语句的拼装。 #####2.不定个数参数in支持 在查询当中,有的时候会碰到类似`select * from user where id in (?,?)`这种情况,如果参数的个数是不确定的,则只能手动拼接sql字符串。如果碰到这样的需求,jfire-orm支持自动的参数分解和拼接。举个例子,代码如下 ```java /** *ids为1,2,3,形式的字符串,框架会自动将逗号进行区隔,形成参数,然后以字符串的形式放入。 */ select * from user where userid in ~{ids} public List list(String ids); ``` 上面这个例子中,如果是ids的值为`1,2,3,`则sql语句最终会被解析成`select * from user where userid in (?,?,?)` 不仅仅支持1,2,3这种形式。而且还直接支持数组等形式。如果给定的参数是一个数组或者经过参数运算返回的是一个数组,程序也能够将其进行拆分形成(?,?,?)这样的形式