說到springBoot架構,我們常常想到的就是它的優點,像快速建構項目;内嵌了servlet容器;降低了對環境的要求;提供運作時的應用監控;代碼少了、配置檔案少了、減少了開發時間和提高了工作效率 這些客套話的優點我們可以忽略不計,我們實際需要與最常用的優點是springBoot可以快速整合第三方架構(如Spring JDBC,Spring ORM,Spring Data,Spring Security等,本文我們要聊的就是springBoot整合Spring ORM的應用。
無論是新架構還是舊架構都有它的優點和缺點存在,它的最大的缺點我認為是因為整合的東西比較多,開發過程中假如出現錯誤不好定位不好處理,夾道迎接優點誠懇包容缺點,讓我們開始吧。
本文資料庫表
1 college 學院表
2 teacher 教師表(外鍵關聯學院)
3 student 學生表(外鍵關聯學院表與教師表)
使用mybatis自動生成工具生成資料庫orm資訊
mybatis orm自動生成工具是由mybatis-generator-core-&&.jar 建構的一套生成工具,生成的資料類别有資料庫表model、資料庫表modelExample、資料庫表Mapper.java接口映射檔案、資料庫表Mapper.xml 腳本映射檔案,其中modelExample聚合了資料庫常用的關鍵字與符号操作,比如=、>、not in、between 等等,目錄如下:
這三個包或目錄建的位置沒有嚴格的要求,但與配置息息相關,是以最好命名和建的位置清晰可見。目錄或包建好生成就簡單了,直接執行生成工具main函數即可:
/**
* mybatis orm自動生成工具
*/
public class Generator {
public static void main(String[] args) throws Exception {
//MBG 執行過程中的警告資訊
List<String> warnings = new ArrayList<String>();
//當生成的代碼重複時,覆寫原代碼
boolean overwrite = true;
//讀取我們的 MBG 配置檔案
InputStream is = Generator.class.getResourceAsStream("/GeneratorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//建立 MBG
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//執行生成代碼
myBatisGenerator.generate(null);
//輸出警告資訊
for (String warning : warnings) {
System.out.println(warning);
}
}
}
InputStream is = Generator.class.getResourceAsStream("/GeneratorConfig.xml");
如上的代碼最關鍵的是在這一小段,讀取配置檔案,該配置檔案就決定了資料庫的連接配接資訊和生成檔案的存放目錄
如下是該配置檔案的代碼段:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="generator.properties"/>
<context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 為模型生成序列化方法-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 為生成的Java模型建立一個toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!--自定義注釋生成器-->
<commentGenerator type="com.allen.springBoot.mybatis.autogenerator.CommentGenerator">
<!-- 是否去除自動生成的注釋 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.fxms.connectionURL}"
userId="${jdbc.userId}"
password="${jdbc.password}">
</jdbcConnection>
<!--orm表model存放目錄-->
<javaModelGenerator targetPackage="com.allen.springBoot.mybatis.dao.model" targetProject="src\main\java"/>
<!--orm腳本映射檔案存放目錄-->
<sqlMapGenerator targetPackage="mappers" targetProject="src\main\resources"/>
<!--orm接口映射存放目錄-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.allen.springBoot.mybatis.dao.mapper"
targetProject="src\main\java"/>
<!--生成全部表tableName設為%-->
<table tableName="%">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>
該配置檔案最主要的内容是model存在路徑、腳本映射檔案存放目錄和接口存放路徑的配置。其中有一小段是注釋生成器,可以給model對應的字段添加上建表時的注釋, java代碼段如下:
/**
* 自定義注釋生成器
*/
public class CommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments = false;
/**
* 設定使用者配置的參數
*/
@Override
public void addConfigurationProperties(Properties properties) {
super.addConfigurationProperties(properties);
this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
}
/**
* 給字段添加注釋
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
String remarks = introspectedColumn.getRemarks();
//根據參數和備注資訊判斷是否添加備注資訊
if(addRemarkComments&&StringUtility.stringHasValue(remarks)){
//文檔注釋開始
field.addJavaDocLine("/**");
//擷取資料庫字段的備注資訊
String[] remarkLines = remarks.split(System.getProperty("line.separator"));
for(String remarkLine:remarkLines){
field.addJavaDocLine(" * "+remarkLine);
}
addJavadocTag(field, false);
field.addJavaDocLine(" */");
}
}
}
寫到這邊工作就差不多完成一半了,接下來主要就是做springBoot的一些配置,首先掃描mapper接口所在包路徑,這可以在springBoot啟動類 用@MapperScan注解進行配置,如下所示
@SpringBootApplication
@ComponentScan("com.allen.springBoot.*") //元件包掃描路徑
@MapperScan("com.allen.springBoot.mybatis.dao.mapper")//掃描mybatis mapper接口所在包
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
接下來就是資料庫連接配接池和工廠的建立了,首先建立資料庫連接配接池,這邊使用HikariCP進行連接配接池建立,如下連接配接池的類
@Configuration
@EnableTransactionManagement
public class DataBaseConfiguration implements EnvironmentAware {
private RelaxedPropertyResolver propertyResolver;
private static Logger log = LoggerFactory
.getLogger(DataBaseConfiguration.class);
private Environment env;
@Override
public void setEnvironment(Environment env) {
this.env = env;
this.propertyResolver = new RelaxedPropertyResolver(env, "jdbc.");
}
/**
* 資料庫的參數配置
* @return
*/
@Bean(destroyMethod = "shutdown")
public DataSource dataSource() {
log.debug("Configruing DataSource");
if (propertyResolver.getProperty("url") == null
&& propertyResolver.getProperty("databaseName") == null) {
log.error("Your database conncetion pool configuration is incorrct ! The application "
+ "cannot start . Please check your jdbc");
Arrays.toString(env.getActiveProfiles());
throw new ApplicationContextException(
"DataBase connection pool is not configured correctly");
}
HikariConfig config = new HikariConfig();
config.setDataSourceClassName(propertyResolver
.getProperty("dataSourceClassName"));
if (propertyResolver.getProperty("url") == null
|| "".equals(propertyResolver.getProperty("url"))) {
config.addDataSourceProperty("databaseName",
propertyResolver.getProperty("databaseName"));
config.addDataSourceProperty("serverName",
propertyResolver.getProperty("serverName"));
} else {
config.addDataSourceProperty("url",
propertyResolver.getProperty("url"));
}
config.setUsername(propertyResolver.getProperty("username"));
config.setPassword(propertyResolver.getProperty("password"));
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource"
.equals(propertyResolver.getProperty("dataSourceName"))) {
config.addDataSourceProperty("cachePrepStmts",
propertyResolver.getProperty("cachePrepStmts"));
config.addDataSourceProperty("prepStmtCacheSize",
propertyResolver.getProperty("prepStmtsCacheSize"));
config.addDataSourceProperty("prepStmtCacheSqlLimit",
propertyResolver.getProperty("prepStmtCacheSqlLimit"));
config.addDataSourceProperty("userServerPrepStmts",
propertyResolver.getProperty("userServerPrepStmts"));
}
return new HikariDataSource(config);
}
}
資料庫連接配接池有了就需要資料庫連接配接的工廠SqlSessionFactory才能建立更多的連接配接資料庫的操作 ,如下是連接配接工廠的類
@Configuration
@ConditionalOnClass({ EnableTransactionManagement.class})
@AutoConfigureAfter({ DataBaseConfiguration.class })
public class MybatisConfiguration implements EnvironmentAware {
private static Log logger = LogFactory.getLog(MybatisConfiguration.class);
private RelaxedPropertyResolver propertyResolver;
@Inject
private DataSource dataSource;
@Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment,
"mybatis.");
}
/**
* mybatis的參數配置
* @return
*/
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory() {
try {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(propertyResolver
.getProperty("typeAliasesPackage"));//mybatis mapper接口所在包路徑
sessionFactory
.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(propertyResolver
.getProperty("mapperLocations")));//mybatis mapper接口映射的xml配置檔案所在路徑
sessionFactory
.setConfigLocation(new DefaultResourceLoader()
.getResource(propertyResolver
.getProperty("configLocation")));//資料庫其他參數配置
return sessionFactory.getObject();
} catch (Exception e) {
logger.warn("Could not confiure mybatis session factory");
return null;
}
}
@Bean
@ConditionalOnMissingBean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource);
}
}
如上資料庫連接配接池和連接配接工廠讀取的配置參數都是從application.yml配置檔案讀取,如下是配置檔案的内容
#端口号
server:
port: 8080
# DATASOURCE
jdbc:
dataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
url: jdbc:mysql://127.0.0.1:3306/test0629?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
databaseName: test0629
serverName: 127.0.0.1
username: root
password: [email protected]
cachePrepStmts: true
prepStmtsSize: 250mybatis-config.xml
prepStmtsCacheSqlLimit: 2048
userServerPrepStmts: true
# MyBatis
mybatis:
typeAliasesPackage: com.allen.springBoot.mybatis.dao.mapper
mapperLocations: classpath:/mappers/*.xml
configLocation: classpath:/mybatis-config.xml
該配置的都配置完成,接下就是進入實戰應用階段 ,mybatis自動生成工具建立了很完善的條件查詢和增删改查的操作,操作起來非常友善,直接在控制層注入mybatis自動生成工具生成的mapper接口即可,如下是基本的crud執行個體:
@Resource
public StudentMapper studentMapper;
/**
* 查詢所有學生資訊
* http://localhost:8080/getAllStudents
*/
@GetMapping("/getAllStudents")
@ResponseBody
public List<Student> getAllStudents(){
StudentExample studentExample = new StudentExample();
StudentExample.Criteria studentExampleCriteria = studentExample.createCriteria();
List<Student> students= studentMapper.selectByExample(studentExample);
return students;
}
/**
* 添加新的學生
* http://localhost:8080/addNewStudent
*/
@GetMapping("/addNewStudent")
@ResponseBody
public String addNewStudent(){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("ss");
String randomNum=simpleDateFormat.format(new Date());
StudentExample studentExample = new StudentExample();
StudentExample.Criteria studentExampleCriteria = studentExample.createCriteria();
Student student=new Student("10009"+randomNum,"李四9"+randomNum,7,1);
try {
studentMapper.insert(student);
return "添加成功 ";
}catch (Exception e){
e.printStackTrace();
return "添加異常:"+e.getMessage();
}
}
/**
* 更新學生資訊
* http://localhost:8080/updateStudentInfo
*/
@GetMapping("/updateStudentInfo")
@ResponseBody
public String updateStudentInfo(){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("ss");
String randomNum=simpleDateFormat.format(new Date());
Student student=new Student(14,"1000988","李四999"+randomNum,7,1);
try {
studentMapper.updateByPrimaryKeySelective(student);//更新方法1,按主鍵更新
StudentExample studentExample = new StudentExample();
StudentExample.Criteria studentExampleCriteria = studentExample.createCriteria();
studentExampleCriteria.andStudentNoEqualTo("1000999");
student=new Student("1000999","李四998"+randomNum,7,1);
studentMapper.updateByExampleSelective(student,studentExample);//更新方法2,按條件更新
return "更新成功";
}catch (Exception e){
e.printStackTrace();
return "更新異常:"+e.getMessage();
}
}
/**
* 通過學号删除學生
* http://localhost:8080/deleteStudentByNo/1000999
*/
@GetMapping("/deleteStudentByNo/{studentNo}")
@ResponseBody
public String deleteStudentByNo(@PathVariable(value = "studentNo") String studentNo){
StudentExample studentExample = new StudentExample();
StudentExample.Criteria studentExampleCriteria = studentExample.createCriteria();
studentExampleCriteria.andStudentNoEqualTo(studentNo);
try {
studentMapper.deleteByExample(studentExample);
return "删除成功";
}catch (Exception e){
e.printStackTrace();
return "删除異常:"+e.getMessage();
}
}
細心的朋友會發現,這些操作隻是針對單表的操作,但我們日常開發過程中,多表關聯查詢是必不可少的,這時候該怎麼辦呢?,不用擔心,自動生成工具雖然幫我們自動生成了很全面的資料庫表映射腳本和操作接口,但不代表不可以修改它,我們仍然可以回歸到原始的mybatis配置方法,添加我們所需要的更複雜sql關聯查詢操作,如下所示在studentMapper.xml添加我們所需要的關聯查詢操作:
<!--查詢指定老師所帶的學生姓名-->
<select id="getStudentNameByTeacherName" resultType="String">
SELECT s.student_name from student s
INNER JOIN teacher t on t.teacher_id=s.teacher_id where t.teacher_name=#{0}
</select>
<!--查詢指定祖籍指定學院下的老師姓名-->
<select id="getTeacherNameByCityNameAndCollectName" resultType="String">
SELECT t.teacher_name from teacher t INNER JOIN college c on c.college_id=t.college_id
where t.city=#{0} and c.colege_name=#{1} ;
</select>
<!--查詢指定學院指定老師所帶的學生學号-->
<select id="getStudentNoByTeacherNameAndCollectName" resultType="String">
SELECT s.student_no from student s INNER JOIN teacher t
on t.teacher_id=s.teacher_id INNER JOIN college c on c.college_id=s.college_id
where t.teacher_name=#{0} and c.colege_name=#{1};
</select>
StudentMapper接口操作方法一樣,添加對應的腳本id映射接口(需要注意傳入參數的個數)
List<String> getStudentNameByTeacherName(String studentName);
List<String> getTeacherNameByCityNameAndCollectName(String city, String collegeName);
List<String> getStudentNoByTeacherNameAndCollectName(String teacherName, String collegeName);
控制層使用調用方式一樣,如下
/**
*通過教師的名字擷取學生的名字
* http://localhost:8080/getStudentNameByTeacherName/張三1
* @return
*/
@GetMapping( "/getStudentNameByTeacherName/{teacherName}")
@ResponseBody
public List<String> getTeacherNameByStudentName(@PathVariable(value = "teacherName") String teacherName) {
List<String> listStudent=studentMapper.getStudentNameByTeacherName(teacherName);
return listStudent;
}
/**
* 查詢祖籍為龍岩市計算機學院的老師姓名
* http://localhost:8080/getTeacherNameByCityNameAndCollectName/龍岩市/計算機學院
*/
@GetMapping("/getTeacherNameByCityNameAndCollectName/{city}/{collegeName}")
@ResponseBody
public List<String> getTeacherNameByCityNameAndCollectName(@PathVariable(value = "city") String city,@PathVariable(value = "collegeName") String collegeName){
List<String> teacherNames=studentMapper.getTeacherNameByCityNameAndCollectName(city,collegeName);
return teacherNames;
}
/**
* 查詢計算機學院下張三1老師帶的學生學号
* http://localhost:8080/getStudentNoByTeacherNameAndCollectName/張三1/計算機學院
*/
@GetMapping("/getStudentNoByTeacherNameAndCollectName/{teacherName}/{collegeName}")
@ResponseBody
public List<String> getStudentNoByTeacherNameAndCollectName(@PathVariable(value = "teacherName") String teacherName,@PathVariable(value = "collegeName") String collegeName){
List<String> studentNos=studentMapper.getStudentNoByTeacherNameAndCollectName(teacherName,collegeName);
return studentNos;
}
寫到這邊工作算是完成了,執行springBoot的main函數可以開始工作了。
一路下來真不容易,無數字的調試,無數次的百度和參考,感謝優秀的網絡資源,如上表述肯定還有不全面不清晰甚至錯誤的地方,歡迎看到的同仁們提出來,互相學習共同成長與進步,謝謝。
本文 GitHub源碼位址 https://github.com/higherzjm/springBoot_mybatis.git,附Mysql腳本