aka-dbutils的Mapper映射器

Mapper(映射器)

上一节已经稍微提到了Mapper映射器的使用,这节会详细介绍。

Mapper(映射器)的作用是为了简化开发Dao类,aka-dbutils会为你根据定义的Mapper动态生成一个继承自Mapper的Dao类,一个Mapper相当于一个Dao接口,实际上Mapper是抽象类,并不是接口,其定义的抽象方法要和其Mapper同名的md文件里的md方法相对应,aka-dbutils根据Mapper生成的动态代理对象会找到这种对应关系,并把通过Mapper接口传入的参数传递到md方法,md方法根据传入的参数运行其方法体定义的拼装SQL语句逻辑从而生成最终的jdbc可以执行的SQL语句。Mapper并不是一个真正的接口,它是一个继承AkaMapper的抽象类,里面可以定义多个对应到md方法的抽象方法,也可以定义具体方法。

下面的例子展示了如何定义一个Mapper,CourseMpper定义如下所示。

package com.github.ulwx.aka.dbutils.demo.dao;

import com.github.ulwx.aka.dbutils.database.AkaMapper;
import com.github.ulwx.aka.dbutils.database.DataBaseSet;
import com.github.ulwx.aka.dbutils.database.MDMethods.One2ManyMapNestOptions;
import com.github.ulwx.aka.dbutils.database.MDMethods.One2OneMapNestOptions;
import com.github.ulwx.aka.dbutils.database.MDMethods.PageOptions;
import com.github.ulwx.aka.dbutils.database.MDMethods.InsertOptions;
import com.github.ulwx.aka.dbutils.database.MDataBase;
import com.github.ulwx.aka.dbutils.demo.domian.Course;
import com.github.ulwx.aka.dbutils.demo.mydomain.One2ManyStudent;
import com.github.ulwx.aka.dbutils.demo.mydomain.One2OneStudent;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

public abstract class CourseMpper extends AkaMapper {

    public abstract  DataBaseSet getRSet(Map<String,Object> ars); 
    public abstract DataBaseSet getRSetPage(Map<String,Object> ars, PageOptions pageOptions);
    public abstract Course getOneCourse(Integer id, String name, Map<String,Object> ars,Course cs);
    public abstract  List<Course> getCoursesByIds(Integer[] ids, String name, Map<String,Object> ars,Course cs);
    public abstract List<Course> getCouseList(String name);
    public abstract List<Course> getCouseListPage(String name,PageOptions pageOptions);
    public abstract List<One2OneStudent> getCouseListOne2One(String name, One2OneMapNestOptions nestOptions);
    public abstract List<One2OneStudent> getCouseListOne2OnePage(String name, One2OneMapNestOptions nestOptions,
                                                        PageOptions pageOptions);
    public abstract List<One2ManyStudent> getCouseListOne2Many(String name, One2ManyMapNestOptions nestOptions);
    public abstract void addCourse(Course course);
    public abstract int addCourseReturnKey(Course course, InsertOptions insertOptions);
    public abstract int updateCourse(Course course);

    public void updateCourseManual(){  
        Course course=new Course();
        course.setName("abcd");
        course.setCreatime(LocalDateTime.now());
        course.setClassHours(45);
        course.setId(3);
        MDataBase mDataBase=this.getMdDataBase(); //可以通过getMdDataBase()方法返回MDataBase实例,从而通过它来进行数据库操作
        Course c = mDataBase.queryOneBy(course);//①
        this.updateCourse(course); //② 可以调用本Mapper里的其它抽象方法
    }

}

上面的CourseMpper类中,CourseMpper继承自AkaMapper类,CourseMpper定义成抽象的,所以其内部可以定义具体的方法,如上面的updateCourseManual()方法,在方法内部可以通过this.getMdDataBase()获取MDataBase实例从而可以通过其内部定义的数据库操作方法来操作数据库(①处),甚至可以调用CourseMpper里定义的其它抽象方法(②处)。

需要注意的是如果CourseMapper里的定义的方法(抽象方法和非抽象方法)被调用时如果没有运行在事务里,则方法里的每一次调用的数据库操作方法,如调用MDataBase#queryOneBy()方法,或者通过抽象方法间接调用MDataBase里的某个数据库操作方法(实际上每个抽象方法都会映射到MDataBase里某个数据库操作方法,如CourseMpper#updateCourse()会映射到CourseMpper#update()数据库操作方法),每次的数据库操作方法都会首先获取一个新的连接,执行完这个操作后会自动关闭这个连接。如果CourseMapper里定义的方法被调用处于事务中,则在事务里所有CourseMapper里方法的调用,不管是否多次,都是使用同一个连接。

package com.github.ulwx.aka.dbutils.demo.dao;
....
public class CourseMpperTest {
    public static String DbPoolName="dbutils-demo";
   ....
    public static void testUpdateCourseManual(){
        MDbUtils.getMapper(DbPoolName, CourseMpper.class).updateCourseManual();
    }
    public static void main(String[] a) {
        InitDataTool.prepareData();;
         testUpdateCourseManual();
    }
}

上面的例子都来自aka-dbutils-demo工程,下载地址:https://github.com/ulwx/aka-dbutils-demo,你可以下载下来调试运行。在上面的例子中通过了MDbUtils#getMapper()方法调用生成了CourseMpper映射器的动态代理对象,然后在动态代理对象上调用映射器里定义的方法,动态代理对象实现了映射器里定义的抽象方法,使之与md文件的相应方法产生关联,从而执行md方法里的拼装SQL语句逻辑,最终生成jdbc可以执行的预编译SQL语句(带问号)。

下面对Mapper里如何定义抽象方法的规则进行介绍。

方法类别参数类型返回类型
1返回单值查询记录方法(select)java原始类型及其封装类型,数组及List类型(元素为原始类型及其封装类型),String,自定义JavaBean,Map<String,Object>类型,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimal自定义JavaBean,String,原始类型及其封装类型,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimal
2返回多值的查询记录方法(select)java原始类型及其封装类型,数组类型(元素为原始类型及其封装类型),String,自定义JavaBean,Map<String,Object>类型,,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimal,PageOptions,One2OneMapNestOptions,One2ManyMapNestOptionsDataBaseSet,List,其中X为:Map<String,Object>,自定义JavaBean,原始类型的封装类型、String,java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimal
3更新记录方法(update)java原始类型及其封装类型,数组类型(元素为原始类型及其封装类型),String,自定义JavaBean,Map<String,Object>类型,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimalint,Integer,long,Long,void。更新方法的返回类型如果为int,Integer,Long,long则表示是更新的条数;如果为void,表示不返回值
4删除记录方法(delete)java原始类型及其封装类型,数组类型(元素为原始类型及其封装类型),String,自定义JavaBean,Map<String,Object>类型,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimalint,Integer,long,Long,void。删除方法的返回类型如果为int,Integer,Long,long则表示是删除的条数;如果为void,表示不返回值
5添加记录方法(insert)java原始类型及其封装类型,数组类型(元素为原始类型及其封装类型),String,自定义JavaBean,Map<String,Object>类型,java日期类型(LocalDate、LocalDateTime、LocalTime、Date),java.sql下日期类型(Time、Date、Timestamp),BigInteger,BigDecimal,InsertOptionsint,Integer,long,Long,void。添加记录方法的返回类型如果为int,Integer,Long,long,如果方法的InsertOptions参数为null或没有指定,则代表的是删除的条数;如果指定了InsertOptions并且指定了ReturnFlag.AutoKey,则代表返回的主键id;

上述表格展示了如何定义一个Mapper的抽象方法,约定了各种方法类别所允许的参数类型和返回类型。需要说明的是方法传递的参数最终会转换为Map<String,Object>类型传入到md方法里,例如:

public abstract Course getOneCourse(Integer id,  String name,  Map<String,Object> args, Course cs);

getOneCourse()方法的所有参数都会合起来转换成一个最终的Map<String,Object>类型对象。针对上面表格里提到的原始类型(及封装类型)、String类型、数组类型、日期类型、BigInteger、BigDecimal会使用参数名称作为Map的key,值作为Map的value添加到最终的Map<String,Object>对象;针对自定义JavaBean(Course)会把其转换为一个Map<String,Object>对象添加到最终的Map<String,Object>对象;如果参数本身是Map<String,Object>类型,则不需要转换,直接添加到最终的Map<String,Object>对象。参数添加的顺序会按照声明的顺序添加,所以后面的相同key的项会覆盖前面的。下面通过调用getOneCourse()方法的程序片段来说明生成Map<String,Object>的过程,程序片段如下:

Map<String,Object> arg = new HashMap<>();
arg.put("teacherId","1");
Course cs=new Course();
cs.setCreatime(createTime);
Course course= MDbUtils.getMapper(DbPoolName, CourseMpper.class).getOneCourse( 2,"course", arg, cs);

上面的程序片段执行时在执行getOneCourse()方法时内部生成Map<String,Object>过程如下:

Map<String,Object> finalMap = new HashMap<>();
finalMap.put("id",2);
finalMap.put("name","course");
finalMap.putAll(arg);
Map<String,Object> courseMap=MD.map(cs); //把Course对象转换成Map<String,Object>对象,属性名为key,属性值为value
finalMap.putAll(courseMap)

上面的过程可以看出Map<String,Object>如果存在相同key的添加,后面会覆盖前面的,例如上面的Course cs对象由于存在id属性,cs会被转换成Map<String,Object>对象被添加到finalMap中,从而覆盖了前面的key为"id"的记录,所以我们在定义参数的时候要注意。还有一个需要注意的是如果抽象方法里定义的简单类型参数(原始类型(及封装类型)、String类型、数组类型、日期类型、BigInteger、BigDecimal),则参数名是作为最终Map<String,Object>对象的key,但由于java类在程序编译后的class里不会包含参数的名称信息,所以要在编译时指定-parameters编译选项才能保留参数名称信息在class里,如在maven的pom.xml里指定:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.8.0</version>
   <configuration>
      <source>${java.source.version}</source>
      <target>${java.target.version}</target>
      <compilerArgs>
         <arg>-parameters</arg>
      </compilerArgs>
   </configuration>
</plugin>

当然你也可以在你的集成开发环境里指定-parameters编译选项。如果你的环境不允许你设置-parameters编译选型,你可以通过@AkaParam注解来显示在参数旁指定Map<String,Object>里的key(即参数名称),例如:

public abstract Course getOneCourse(@AkaParam("id") Integer id,  @AkaParam("name")String name, 
                                      Map<String,Object> args, Course cs);

注意Map<String,Object>类型参数和自定义JavaBean类型参数不需要指定@AkaParam,它们的参数名称可以任意选择,因为它们的参数名没有作用。

最终finalMap会传入CourseMpper类所对应的md文件所生成的Md类的相应方法—getOneCourse(Map<String, Object> args)方法,如下所示:

package com.github.ulwx.aka.dbutils.mysql.dao.db_student;
import java.util.Map;
import com.github.ulwx.aka.dbutils.database.nsql.NFunction;
import com.github.ulwx.aka.dbutils.database.nsql.MDTemplate;
import com.github.ulwx.aka.dbutils.database.nsql.MDMehtodOptions;
import static com.github.ulwx.aka.dbutils.database.nsql.NFunction.*;
public class CourseMpperMd {
	....
	public static String getOneCourse(Map<String, Object> args)throws Exception{
		String retString="";
		MDMehtodOptions options = new MDMehtodOptions();
		options.setSource(trimRight("CourseMpperMd",2)+".md:getOneCourse");
		retString=retString+" SELECT";
		retString=retString+" NAME,";
		retString=retString+" class_hours";
		retString=retString+" FROM";
		retString=retString+" course";
		retString=retString+" WHERE 1 = 1";
		if(  NFunction.isNotEmpty(args.get("name"))  ){
			retString=retString+" and name like #{name%}";
		}
		if(  NFunction.isNotEmpty(args.get("teacherId"))  ){
			retString=retString+" and teacher_id=#{teacherId}";
		}
		return retString;
	}
	....
}



下面通过来例子说明如何编写Mapper。

package com.github.ulwx.aka.dbutils.mysql.dao.db_student;
......
public abstract class CourseMpper extends AkaMapper {

    public abstract  DataBaseSet getRSet(Map<String,Object> ars);
    public abstract DataBaseSet getRSetPage(Map<String,Object> ars, PageOptions pageOptions);
    public abstract String getOneString(String name);
    public abstract Integer getOneInteger(String name);
    public abstract BigInteger getOneBigInteger(String name);
    public abstract List<BigInteger> getOneBigIntegerList(String name);
    public abstract LocalDateTime getOneLocalDateTime();
    public abstract Timestamp getOneTimestamp();
    public abstract List<Timestamp> getOneTimestampList();
    public abstract Integer getOneIntegerReturnNull(String name);
    public abstract Course getOneCourse(Integer id, String name, Map<String,Object> ars, Course cs);
    public abstract  List<Course> getCoursesByIds(Integer[] ids, String name, Map<String,Object> ars,Course cs);
    public abstract List<Course> getCouseList(String name);
    public abstract List<Course> getCouseListPage(String name,PageOptions pageOptions);
    public abstract void addCourse(Course course);
    public abstract int addCourseReturnKey(Course course, InsertOptions insertOptions);
    public abstract int updateCourse(Course course);
    public abstract  void dropCourse();

    public void updateMyCourse(){
        Course course=new Course();
        course.setName("course1");
        course.setClassHours(11);
        MDataBase mDataBase=this.getMdDataBase();
        //默认每次执行完数据库操作后关闭数据库连接
        List<Course> list = mDataBase.queryListBy(course);
        course.setId(1);
        //默认每次执行完数据库操作后关闭数据库连接
        this.updateCourse(course);
    }
    public void updateMyCourseIntrans(){
        Course course=new Course();
        course.setName("course1");
        course.setClassHours(11);
        MDataBase mDataBase=this.getMdDataBase();
        try {
            mDataBase.setAutoCommit(false);//设置启动事务标志
            List<Course> list = mDataBase.queryListBy(course);
            course.setId(1);
            this.updateCourse(course);
            mDataBase.commit();
        }catch (Exception e){
            mDataBase.rollback();;
        }finally{
            mDataBase.close();
        }
    }
}



CourseMpper.md文件

getRSet
====
select * from course where 1=1
@if( $$:name ){
and name like #{name%}
@}
order by id asc


getRSetPage
====
select * from course where 1=1
@if( $$:name ){
and name like #{name%}
@}
@if( $$:classHours ){
and class_hours in(#{classHours})
@}
order by id

getOneCourse
===
SELECT
NAME,
class_hours    /* 和 class_hours as classHours 等效,classHours对应到JavaBean的属性名 */
FROM
course
WHERE 1 = 1
@if( $$:name ){
and name like #{name%}
@}
@if( $$:teacherId ){
and teacher_id=#{teacherId}
@}

getCoursesByIds
===
select id,name ,class_hours as classHours,creatime from course where 1=1
@if( $$:name ){
and name like #{name%}
@}
@if($$:ids){
and id in (#{ids})
@}
order by id asc

getCouseList
====
select * from course where 1=1
@if( $$:name ){
and name like #{name%}
@}

getCouseListPage
====
select * from course where 1=1
@if( $$:name ){
and name like #{name%}
@}
order by id 

getCouseListPageCount
====
select count(1) from course where 1=1
@if( $$:name ){
and name like #{name%}
@}


addCourse
====
INSERT INTO `course` (
`name`,
`class_hours`,
`creatime`
)
VALUES
(
#{name},
#{classHours},
#{creatime}
)


addCourseReturnKey
====
INSERT INTO `course` (
`name`,
`class_hours`,
`creatime`
)
VALUES
(
#{name},
#{classHours},
#{creatime}
)

updateCourse
====
UPDATE
`course`
SET
`name` = #{name},
`class_hours` = #{classHours},
`creatime` = #{creatime}
WHERE `id` = #{id}

dropCourse
====
drop table `course`

getOneString
===
select #{name}

getOneInteger
===
select count(#{name})

getOneIntegerReturnNull
===
select null

getOneBigInteger
===
select 123

getOneBigIntegerList
===
select 1
union
select 2
union
select 3

getOneLocalDateTime
===
SELECT STR_TO_DATE('2014-04-22 15:47:06','%Y-%m-%d %H:%i:%s')

getOneTimestamp
===
SELECT STR_TO_DATE('2014-04-22 15:47:06','%Y-%m-%d %H:%i:%s')

getOneTimestampList
===
SELECT STR_TO_DATE('2014-04-22 15:47:06','%Y-%m-%d %H:%i:%s')
union all
SELECT STR_TO_DATE('2015-04-22 15:47:06','%Y-%m-%d %H:%i:%s')

CourseMpperTest

package com.github.ulwx.aka.dbutils.mysql.dao.db_student;
....

/**
 * 测试Mapper映射器的用法
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class CourseMpperTest {
    public static String DbPoolName="db_student";
    static LocalDateTime createTime =
            LocalDateTime.parse("2021-03-15 22:31:48", CTime.DTF_YMD_HH_MM_SS);
    @Before
    public void setup(){
        Utils.importDbStudent();
    }
    @Test
    public  void testGetOne(){
        Map<String,Object> arg = new HashMap<>();
        arg.put("teacherId","1");
        Course cs=new Course();
        cs.setCreatime(createTime);
        Course course= MDbUtils.getMapper(DbPoolName, CourseMpper.class).getOneCourse(
                2,
                "course",
                 arg,
                //cs里的name属性覆盖了前面name形参声明的参数,所以md文件里的name参数引用的值为null。
                // 同时cs里的teacherId属性覆盖了arg里的teacherId参数的值,所以md文件里tearcherId参数的引用的值也为null
                 cs);
    }
    @Test
    public  void testGetCourseListByIds(){
        Map<String,Object> arg = new HashMap<>();
        arg.put("teacherId","1");
        Course cs=new Course();
        cs.setCreatime(createTime);
        List<Course> courseList=MDbUtils.getMapper(DbPoolName, CourseMpper.class).getCoursesByIds(
                new Integer[]{1,3},
                "course",//①
                arg, //
                cs);//cs里的name属性代表的参数覆盖了①处代表的参数,由于cs的name属性值为null,所以md文件里对name参数引用的值为null
    }
    @Test
    public  void testGetList(){
        List<Course> courseList=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getCouseList("course");
    }
    @Test
    public void testGetOneSimpleType(){
        String ret=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneString("abc");

        Integer ret2=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneInteger("abc");

        Integer ret3=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneIntegerReturnNull("abc");

        BigInteger ret4=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneBigInteger("abc");

        List<BigInteger> ret5=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneBigIntegerList("abc");

        LocalDateTime ret6=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneLocalDateTime();

        Timestamp ret7=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneTimestamp();

        List<Timestamp> ret8=MDbUtils.getMapper(DbPoolName,
                CourseMpper.class).getOneTimestampList();
    }
    @Test
    public  void testCouseListPage(){
        List<Course> courseList=MDbUtils.getMapper(DbPoolName, CourseMpper.class).
                getCouseListPage("course",
                MD.ofPage(2, 3, null));

        List<Course> courseList2=MDbUtils.getMapper(DbPoolName, CourseMpper.class).
                getCouseListPage("course",
                MD.ofPage(2, 3,MD.md(CourseMpper.class,
                        "getCouseListPageCount") ,null));
    }
    @Test
    public  void  testReturnDataBaseSet(){
        Map<String,Object> arg = new HashMap<>();
        arg.put("name","course2");
        List<Course> courseList=new ArrayList();
        DataBaseSet rs=(DataBaseSet)MDbUtils.getMapper(DbPoolName, CourseMpper.class).getRSet(arg);
       
    }

    @Test
    public  void testReturnDataBaseSetAndPage(){
        Map<String,Object> args = new HashMap<>();
        args.put("name","course");
        args.put("classHours", new Integer[]{10,11,12,13,14,15,16,17,18,19});

        PageOptions pageOptions=MD.ofPage(2, 3, null);
   
        DataBaseSet rs=(DataBaseSet)MDbUtils.
                getMapper(DbPoolName, CourseMpper.class).getRSetPage(args,pageOptions);
    
    }

    @Test
    public  void testAddCourse() {
        LocalDateTime localDateTime = LocalDateTime.parse("2021-03-15 22:31:48", CTime.DTF_YMD_HH_MM_SS);
        Course course=new Course();
        course.setName("abcdefg");
        course.setCreatime(localDateTime);
        course.setClassHours(45);
    
        MDbUtils.getMapper(DbPoolName, CourseMpper.class).addCourse(course);
      

    }
    @Test
    public  void testAddCourseAndReturnKey() {
        LocalDateTime localDateTime = LocalDateTime.parse("2021-03-15 22:31:48", CTime.DTF_YMD_HH_MM_SS);
        Course course=new Course();
        course.setName("abcdefg");
        course.setCreatime(localDateTime);
        course.setClassHours(45);
    
        int key=MDbUtils.getMapper(DbPoolName, CourseMpper.class).addCourseReturnKey(course
                , MD.ofInsert(true));
      
    }
    @Test
    public  void testUpdateCourse() {
        LocalDateTime localDateTime = LocalDateTime.parse("2021-03-15 22:31:48", CTime.DTF_YMD_HH_MM_SS);
        Course course=new Course();
        course.setName("abcdefg");
        course.setCreatime(localDateTime);
        course.setClassHours(45);
        course.setId(3);
   
        CourseMpper courseMpper=MDbUtils.getMapper(DbPoolName, CourseMpper.class);
        //courseMpper 返回的mapper对象不是线程安全的
        courseMpper.updateCourse(course);
        courseMpper.updateCourse(course);
  
        courseMpper=MDbUtils.getMapper(DbPoolName, CourseMpper.class);
        courseMpper.updateCourse(course);
       
    }
    @Test
    public  void testDropCourse() {
       
        MDbUtils.getMapper(DbPoolName, CourseMpper.class).dropCourse();

    }
   ....
    @After
    public void after(){
        DbContext.removeDebugSQLListener();
    }
}

上面的例子摘自aka-dbutils工程,程序代码在aka-dbutils/src/test/java/com/github/ulwx/aka/dbutils/mysql/dao/db_student下。

在Spring里也可以使用Mapper,你需要配置一个AkaMpperScannerConfigurer Bean,它的作用为扫描所有继承自AkaMapper的Mapper(映射器),并在Spring容器启动的时候为每个Mapper生成动态代理Bean,并注册到Spring容器里。下面以SpringBoot为例讲解Mapper集成到Spring的用法。

下面所有的示例来自于aka-dbutils-spring-boot-starter-test工程,你可以到https://github.com/ulwx/aka-dbutils-spring-boot-starter-test下载。

1.需要引入pom.xml

    <dependencies>
        ....
        <dependency>
            <artifactId>aka-dbutils-spring-boot-starter</artifactId>
            <groupId>com.github.ulwx</groupId>
            <version>1.0.3.1</version>
        </dependency>
        ....
    </dependencies>

2.首先需要在配置类里定义一个AkaMpperScannerConfigurer

MyConfiguration类

package com.github.ulwx.aka.dbutils.database.spring.boot.starter.test;

import com.github.ulwx.aka.dbutils.database.spring.AkaMpperScannerConfigurer;
import com.github.ulwx.aka.dbutils.database.spring.MDataBaseFactory;
import com.github.ulwx.aka.dbutils.database.spring.MDataBaseTemplate;
....
@EnableTransactionManagement(proxyTargetClass = true)
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration
@ComponentScan
public class MyConfiguration implements ApplicationContextAware {
    private ApplicationContext ctx;
    @Bean(destroyMethod = "close")
    public BasicDataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?x=1&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("abcd");
        dataSource.setMaxWaitMillis(10000);
        dataSource.setInitialSize(1);
        dataSource.setMaxTotal(10);
        dataSource.setMinEvictableIdleTimeMillis(6000);
        return dataSource;
    }
    @Bean
    public DataSourceTransactionManager transactionManager() {
        DataSourceTransactionManager dt = new DataSourceTransactionManager();
        dt.setDataSource(dataSource());//①
        return dt;
    }
    //配置MDataBaseTemplate类型Bean,如果不配置,aka-dbutils-spring-boot-starter会自动配置一个
    @Bean
    public MDataBaseFactory mDataBaseFactory() {
        MDataBaseFactory mDataBaseFactory = new MDataBaseFactory(dataSource());//② 和①处的必须注入同一个连接池
        mDataBaseFactory.setTableColumRule(DbConst.TableNameRules.underline_to_camel);
        mDataBaseFactory.setTableNameRule(DbConst.TableColumRules.underline_to_camel);
        return mDataBaseFactory;

    }
    @Bean
    //配置MDataBaseTemplate Bean用于数据库操作,如果不配置,aka-dbutils-spring-boot-starter会自动配置一个
    public MDataBaseTemplate mDataBaseTemplate() {
        MDataBaseFactory mDataBaseFactory=mDataBaseFactory();
        return new MDataBaseTemplate(mDataBaseFactory);
    }
    @Bean
    //配置AkaMpperScannerConfigurer Bean用于用于扫描Mapper(继承自AkaMapper)从而生成代理Bean注册到Spring容器
    //如果不配置,aka-dbutils-spring-boot-starter会自动配置一个
    public static  AkaMpperScannerConfigurer akaMpperScannerConfigurer(){
        AkaMpperScannerConfigurer akaMpperScannerConfigurer=
                new AkaMpperScannerConfigurer();
        //指定扫描的起始包,所有子孙包都会被扫描
        akaMpperScannerConfigurer.setBasePackage(MyConfiguration.class.getPackage().getName());
        //指定MDataBaseTemplate Bean的名称,如果不指定,默认使用"mDataBaseTemplate"名称
        akaMpperScannerConfigurer.setMdDataBaseTemplateBeanName("mDataBaseTemplate");
        return akaMpperScannerConfigurer;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx=applicationContext;
    }
}

3. 定义一个映射器AddressMapper,映射器必须继承自AkaMapper

package com.github.ulwx.aka.dbutils.database.spring.boot.starter.test;

import com.github.ulwx.aka.dbutils.database.AkaMapper;

public abstract class AddressMapper extends AkaMapper {
    public abstract void updateMd();
    public void update(){
        this.updateMd();
    }
    public void query(){
        Address address=new Address();
        address.setAddressId(1);
        this.getMdDataBase().queryOneBy(address); //通过MDataBase实例操作数据库
         this.updateMd(); //可以调用本Mapper里的其它抽象方法
    }
}

4.定义AddressMapper映射器对应的md文件,文件必须处于相同目录中,并且文件名称和类名相同,即AddressMapper.md

getListMd
===
select * from address

updateMd0
===
UPDATE
`address`
SET
`name` = CONCAT('',now())
WHERE  `address_id` = 0

updateMd1
===
UPDATE
`address`
SET
`name` = CONCAT('',now())
WHERE  `address_id` = 1

5.编写Service用于注入Mapper

package com.github.ulwx.aka.dbutils.database.spring.boot.starter.test;

import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class MyService {
    ....
    private AddressMapper mapper; //注入AddressMapper的动态代理Bean
    public MyService(AddressDao addressDao,AddressMapper mapper) {
        ....
        this.mapper=mapper;
    }
    ....
    @Transactional(propagation = Propagation.REQUIRED)
    public void testMapper(){
         mapper.updateMd();
         mapper.update();
         mapper.query();
    }

}

6.编写SpringBoot程序启动入口

package com.github.ulwx.aka.dbutils.database.spring.boot.starter.test;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(TestApplication.class);
        application.run(args);;
    }

   @Bean
   public ApplicationRunner runner(MyService myService){

        return args -> {
            myService.testMapper();
            ....
            System.out.println("ok!");
        };
    }

}

针对SpringBoot,你甚至不用定义一个AkaMpperScannerConfigurer Bean,aka-dbutils-spring-boot-starter会为你自动配置一个,扫描Mapper的包路径为SpringBoot入口类所在的包路径,这里所说的入口类即定义了@SpringBootApplication的类,本例中为TestApplication类所在的包路径。

如果你不使用SpringBoot,只想与Spring集成,集成方式和MyConfiguration类里内容的相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值