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,One2ManyMapNestOptions | DataBaseSet,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,BigDecimal | int,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,BigDecimal | int,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,InsertOptions | int,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类里内容的相同。