个人随笔
目录
Spring+MyBatis+Druid+数据库连接信息和自定义配置文件外放小程序(jar)完美模板
2020-09-11 22:19:29

学任何东西,做任何事情,都得先有原因,否则没有意义,就比如各种框架存在的意义。现在有一种经常遇到的情况,我们经常需要有一个独立的程序来处理一些业务逻辑,也就是小程序(此小程序非微信小程序)。这种小程序有很多,并且每一个都是独立运行,处理独立的业务逻辑,比如定时处理订单的明细数据,定时推送什么的。这种情况下小程序的要求很简单,可以连接数据库即可。


这里用Java小程序来实现,目的是可以连接数据库,控制事物即可,当然后续连接redis、rocketmq也是可以的。那么当然会想得到用spring控制事物,mybatis来操作数据库,数据库连接池用阿里的druid。


我们通常会遇到一个问题,那就是这种小程序打包成jar包后,进程会把一些配置文件,比如数据库链接,自定义的配置文件都打包到jar包里面,但是开发、测试、生产这些东西都是不同的,岂不是我们每次打包之前都要改动配置文件?这样子也太不科学了吧。


目的

  1. 搭建一个java小程序模板(可执行jar),用spring管理事务,mybatis操作数据库,用阿里的连接池druid
  2. 右键run将该maven打包成一个可执行jar,包括spring依赖都打包成一个jar

项目结构


数据库链接信息以及自定义的配置文件都放在jar包外面也就是app.properties

1、pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  2. <modelVersion>4.0.0</modelVersion>
  3. <groupId>com.gdpost</groupId>
  4. <artifactId>app-template</artifactId>
  5. <version>0.0.1-SNAPSHOT</version>
  6. <properties>
  7. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  8. <spring.version>5.1.6.RELEASE</spring.version>
  9. </properties>
  10. <dependencies>
  11. <!--Spring框架核心库 -->
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-context</artifactId>
  15. <version>${spring.version}</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework</groupId>
  19. <artifactId>spring-core</artifactId>
  20. <version>${spring.version}</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework</groupId>
  24. <artifactId>spring-beans</artifactId>
  25. <version>${spring.version}</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework</groupId>
  29. <artifactId>spring-aop</artifactId>
  30. <version>${spring.version}</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>org.springframework</groupId>
  34. <artifactId>spring-jdbc</artifactId>
  35. <version>${spring.version}</version>
  36. </dependency>
  37. <!-- spring整合oracle -->
  38. <dependency>
  39. <groupId>com.oracle</groupId>
  40. <artifactId>ojdbc6</artifactId>
  41. <version>12.1.0.1-atlassian-hosted</version>
  42. </dependency>
  43. <!-- spring整合mysql,和上面不冲突 -->
  44. <dependency>
  45. <groupId>mysql</groupId>
  46. <artifactId>mysql-connector-java</artifactId>
  47. <version>5.1.38</version>
  48. </dependency>
  49. <dependency>
  50. <groupId>com.alibaba</groupId>
  51. <artifactId>druid</artifactId>
  52. <version>1.1.12</version>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.mybatis</groupId>
  56. <artifactId>mybatis</artifactId>
  57. <version>3.4.1</version>
  58. </dependency>
  59. <dependency>
  60. <groupId>org.mybatis</groupId>
  61. <artifactId>mybatis-spring</artifactId>
  62. <version>1.3.0</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>log4j</groupId>
  66. <artifactId>log4j</artifactId>
  67. <version>1.2.17</version>
  68. </dependency>
  69. </dependencies>
  70. <build>
  71. <plugins>
  72. <plugin>
  73. <artifactId>maven-compiler-plugin</artifactId>
  74. <configuration>
  75. <source>1.6</source>
  76. <target>1.6</target>
  77. </configuration>
  78. </plugin>
  79. <plugin>
  80. <groupId>org.apache.maven.plugins</groupId>
  81. <artifactId>maven-shade-plugin</artifactId>
  82. <version>1.4</version>
  83. <configuration>
  84. <createDependencyReducedPom>false</createDependencyReducedPom>
  85. </configuration>
  86. <executions>
  87. <execution>
  88. <!-- 执行package的phase -->
  89. <phase>package</phase>
  90. <!-- 为这个phase绑定goal -->
  91. <goals>
  92. <goal>shade</goal>
  93. </goals>
  94. <configuration>
  95. <!-- 过滤掉以下文件,不打包 :解决包重复引用导致的打包错误-->
  96. <filters>
  97. <filter>
  98. <artifact>*:*</artifact>
  99. <excludes>
  100. <exclude>META-INF/*.SF</exclude>
  101. <exclude>META-INF/*.DSA</exclude>
  102. <exclude>META-INF/*.RSA</exclude>
  103. </excludes>
  104. </filter>
  105. </filters>
  106. <transformers>
  107. <transformer
  108. implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
  109. <resource>META-INF/spring.handlers</resource>
  110. </transformer>
  111. <!-- 打成可执行的jar包 的主方法入口-->
  112. <transformer
  113. implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
  114. <mainClass>com.gdpost.App</mainClass>
  115. </transformer>
  116. <transformer
  117. implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
  118. <resource>META-INF/spring.schemas</resource>
  119. </transformer>
  120. </transformers>
  121. </configuration>
  122. </execution>
  123. </executions>
  124. </plugin>
  125. </plugins>
  126. <defaultGoal>compile</defaultGoal>
  127. </build>
  128. </project>

这里用的是最新版的spring,以及用mybatis来操作数据库,要注意的是build中的内容,这里使用maven-shade-plugin插件将项目打成可执行的jar包,因为有第三方依赖包spring所以用这种方法可以把所有jar打包成一个可执行jar,当然用其他插件也是可以的。打包方式是maven-build 然后输入pacakege即可。

2、spring-mybatis.xml

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xmlns:tx="http://www.springframework.org/schema/tx"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans
  8. http://www.springframework.org/schema/beans/spring-beans.xsd
  9. http://www.springframework.org/schema/context
  10. http://www.springframework.org/schema/context/spring-context-4.3.xsd
  11. http://www.springframework.org/schema/aop
  12. http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
  13. http://www.springframework.org/schema/tx
  14. http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
  15. <!-- 自动扫描 -->
  16. <context:component-scan base-package="com.gdpost" />
  17. <!-- 引入配置文件:不需要 -->
  18. <!-- <bean id="propertyConfigurer"
  19. class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  20. <property name="location" value="classpath:jdbc.properties" />
  21. </bean> -->
  22. <!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
  23. destroy-method="close">
  24. <property name="driverClassName" value="${driver}" />
  25. <property name="url" value="${url}" />
  26. <property name="username" value="${username}" />
  27. <property name="password" value="${password}" />
  28. </bean> -->
  29. <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
  30. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  31. <property name="dataSource" ref="dataSource" />
  32. <!-- 自动扫描mapping.xml文件 -->
  33. <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
  34. </bean>
  35. <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
  36. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  37. <property name="basePackage" value="com.gdpost.mapper" />
  38. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
  39. </bean>
  40. <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
  41. <bean id="transactionManager"
  42. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  43. <property name="dataSource" ref="dataSource" />
  44. </bean>
  45. <!--支持注解驱动的事务管理,指定事务管理器 -->
  46. <tx:annotation-driven transaction-manager="transactionManager"/>
  47. </beans>

这个配置文件是最重要的配置文件,上面的内容都是必须的,我这里注释掉了数据库连接池对象的生成,因为我把数据库链接的配置信息移动到了外部,所以我这里直接用配置类来生成,如下:

3、DruidDataSourceConfiguration.java

  1. @Configuration
  2. public class DruidDataSourceConfiguration {
  3. private String dbUrl = PropertiesUtil.get("dataSource.url");
  4. private String username =PropertiesUtil.get("dataSource.username");
  5. private String password =PropertiesUtil.get("dataSource.password");
  6. private String driverClassName =PropertiesUtil.get("dataSource.driver");
  7. private int initialSize=Integer.parseInt(PropertiesUtil.get("dataSource.initialSize","5"));
  8. private int minIdle= Integer.parseInt(PropertiesUtil.get("dataSource.minIdle","5"));
  9. private int maxActive= Integer.parseInt(PropertiesUtil.get("dataSource.maxActive","20"));
  10. private int maxWait= Integer.parseInt(PropertiesUtil.get("dataSource.maxWait","60000"));
  11. private int timeBetweenEvictionRunsMillis= Integer.parseInt(PropertiesUtil.get("dataSource.timeBetweenEvictionRunsMillis","60000"));
  12. private int minEvictableIdleTimeMillis= Integer.parseInt(PropertiesUtil.get("dataSource.minEvictableIdleTimeMillis","300000"));
  13. private String validationQuery= PropertiesUtil.get("dataSource.validationQuery","SELECT 1 FROM DUAL");
  14. private boolean testWhileIdle= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testWhileIdle","true"));
  15. private boolean testOnBorrow= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testOnBorrow","false"));
  16. private boolean testOnReturn= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testOnReturn","false"));
  17. private boolean poolPreparedStatements=Boolean.parseBoolean(PropertiesUtil.get("dataSource.poolPreparedStatements","true"));
  18. private int maxPoolPreparedStatementPerConnectionSize= Integer.parseInt(PropertiesUtil.get("dataSource.maxPoolPreparedStatementPerConnectionSize","20"));
  19. //wall, 去掉这个,不然会报sql injection violation
  20. private String filters= PropertiesUtil.get("dataSource.filters","stat,log4j");
  21. private String connectionProperties= PropertiesUtil.get("dataSource.connectionProperties","druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");
  22. @Bean //声明其为Bean实例
  23. @Primary //在同样的DataSource中,首先使用被标注的DataSource
  24. public DataSource dataSource() {
  25. DruidDataSource datasource = new DruidDataSource();
  26. datasource.setUrl(this.dbUrl);
  27. datasource.setUsername(username);
  28. datasource.setPassword(password);
  29. datasource.setDriverClassName(driverClassName);
  30. //configuration
  31. datasource.setInitialSize(initialSize);
  32. datasource.setMinIdle(minIdle);
  33. datasource.setMaxActive(maxActive);
  34. datasource.setMaxWait(maxWait);
  35. datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
  36. datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
  37. datasource.setValidationQuery(validationQuery);
  38. datasource.setTestWhileIdle(testWhileIdle);
  39. datasource.setTestOnBorrow(testOnBorrow);
  40. datasource.setTestOnReturn(testOnReturn);
  41. datasource.setPoolPreparedStatements(poolPreparedStatements);
  42. datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
  43. try {
  44. datasource.setFilters(filters);
  45. } catch (SQLException e) {
  46. e.printStackTrace();
  47. }
  48. datasource.setConnectionProperties(connectionProperties);
  49. return datasource;
  50. }
  51. }

@Configuration 表示这个java类是一个配置类,下面的dataSource就对应配置文件中注释掉的dataSource,这样子就不用从classpath下面获取链接信息啦,直接通过配置文件工具类PropertiesUtil从app.properties中获取,下面我们来看一下配置文件工具类怎么实现的。

4、PropertiesUtil.java

  1. /**
  2. * 用完美的单例模式获取properties的数据,加载为map
  3. * @author forever
  4. *
  5. */
  6. public class PropertiesUtil {
  7. //值可变,引用不可变
  8. private static final Properties PROPERTIES =new Properties();
  9. static{
  10. initProperties();
  11. }
  12. /**
  13. * @param key
  14. * @param defaultValue
  15. * @return
  16. */
  17. public static String get(String key,String defaultValue){
  18. if(PROPERTIES.isEmpty()) {
  19. throw new UnsupportedOperationException("配置未加载");
  20. }
  21. String value =PROPERTIES.getProperty(key, defaultValue);
  22. return value;
  23. }
  24. /**
  25. * 获取默认配置的值,这个值可以动态修改
  26. * @param key
  27. * @return
  28. */
  29. public static String get(String key){
  30. return get(key, "");
  31. }
  32. private static void initProperties(){
  33. InputStream is =null;
  34. try {
  35. //获取当前目录
  36. String property = System.getProperty("user.dir");
  37. //默认是linux os
  38. String fileName = "/app.properties";
  39. //判断是否是windows os
  40. if(System.getProperty ("os.name").contains("Windows")) {
  41. fileName = "\\app.properties";
  42. }
  43. // 读取当前目录下conf配置文件
  44. File file = new File(property+fileName);
  45. PROPERTIES.clear();
  46. is = new FileInputStream(file);
  47. PROPERTIES.load(is);
  48. }catch (IOException e) {
  49. e.printStackTrace();
  50. }finally {
  51. if(is!=null) {
  52. try {
  53. is.close();
  54. } catch (IOException e) {
  55. // TODO Auto-generated catch block
  56. e.printStackTrace();
  57. }
  58. }
  59. }
  60. }
  61. }

上面巧妙的用了这个方法

  1. String property = System.getProperty("user.dir");

来获取当前目录,这样子配置文件就跟jar包分开来啦,完美,并且在static静态代码块中来初始化配置类,这样子在第一次使用的时候就可以初始化了,然后我们来看一下我们的配置类:

5、app.properties

  1. ###数据库链接
  2. dataSource.driver=com.mysql.jdbc.Driver
  3. dataSource.url=jdbc:mysql://localhost:3306/weixinser
  4. dataSource.username=root
  5. dataSource.password=forever
  6. ###连接池配置信息(有默认配置)
  7. #dataSource.initialSize=5
  8. #dataSource.minIdle=5
  9. #dataSource.maxActive=20
  10. #dataSource.maxWait=60000
  11. #dataSource.timeBetweenEvictionRunsMillis=60000
  12. #dataSource.minEvictableIdleTimeMillis=300000
  13. #dataSource.validationQuery=SELECT 1 FROM DUAL
  14. #dataSource.testWhileIdle=true
  15. #dataSource.testOnBorrow=false
  16. #dataSource.testOnReturn=false
  17. #dataSource.poolPreparedStatements=true
  18. #dataSource.maxPoolPreparedStatementPerConnectionSize=20
  19. ###wall, 去掉这个,不然会报sql injection violation
  20. #dataSource.filters=stat,log4j
  21. #dataSource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
  22. ###下面是自定义配置
  23. username=lwh

上面的数据库链接只要换成oracle就对应oracle的数据库,并且链接池信息有默认的,修改的话就放开对应的注释即可。然后自定义配置就写在后面,获取配置信息的方法就是
PropertiesUtil.get("username");我们再看下我们的最重要的SpringUtil

6、SpringUtil.java

  1. public class SpringUtil {
  2. private static ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");
  3. public static Object getBean(String serviceName){
  4. return context.getBean(serviceName);
  5. }
  6. }

这里是在开始用的时候就初始化了spring容器,然后就直接使用啦。
后面我们再列一下业务逻辑要使用的类:

7、User.java、UserMappper.java、UserMapper.xml、UserService.java、UserServiceImpl.java

User.java
  1. public class User{
  2. private Long id;//自增的ID
  3. private String username;//用户名
  4. private String userId;//这个用来存放用户的userId,不要用自增键太不安全
  5. private String password;//用户密码
  6. private String nickname;//昵称
  7. private String imgUrl;//用户头像
  8. private String sex;//用户性别0女,1是男
  9. private String age;//用户年龄
  10. private String create_datetime;//用户创建时间
  11. private String update_datetime;//用户修改时间
  12. private String type;//用户类别0是管理员,1是已登录用户,2是游客
  13. private String remark;//用户备注
  14. private String visible;//用户是否有效
  15. private String value;//预留字段
  16. public Long getId() {
  17. return id;
  18. }
  19. public void setId(Long id) {
  20. this.id = id;
  21. }
  22. public String getUserId() {
  23. return userId;
  24. }
  25. public void setUserId(String userId) {
  26. this.userId = userId;
  27. }
  28. public String getUsername() {
  29. return username;
  30. }
  31. public void setUsername(String username) {
  32. this.username = username;
  33. }
  34. public String getPassword() {
  35. return password;
  36. }
  37. public void setPassword(String password) {
  38. this.password = password;
  39. }
  40. public String getNickname() {
  41. return nickname;
  42. }
  43. public void setNickname(String nickname) {
  44. this.nickname = nickname;
  45. }
  46. public String getImgUrl() {
  47. return imgUrl;
  48. }
  49. public void setImgUrl(String imgUrl) {
  50. this.imgUrl = imgUrl;
  51. }
  52. public String getSex() {
  53. return sex;
  54. }
  55. public void setSex(String sex) {
  56. this.sex = sex;
  57. }
  58. public String getAge() {
  59. return age;
  60. }
  61. public void setAge(String age) {
  62. this.age = age;
  63. }
  64. public String getCreate_datetime() {
  65. return create_datetime;
  66. }
  67. public void setCreate_datetime(String createDatetime) {
  68. create_datetime = createDatetime;
  69. }
  70. public String getUpdate_datetime() {
  71. return update_datetime;
  72. }
  73. public void setUpdate_datetime(String updateDatetime) {
  74. update_datetime = updateDatetime;
  75. }
  76. public String getType() {
  77. return type;
  78. }
  79. public void setType(String type) {
  80. this.type = type;
  81. }
  82. public String getRemark() {
  83. return remark;
  84. }
  85. public void setRemark(String remark) {
  86. this.remark = remark;
  87. }
  88. public String getValue() {
  89. return value;
  90. }
  91. public void setValue(String value) {
  92. this.value = value;
  93. }
  94. public String getVisible() {
  95. return visible;
  96. }
  97. public void setVisible(String visible) {
  98. this.visible = visible;
  99. }
  100. @Override
  101. public String toString() {
  102. return "User [id=" + id + ", username=" + username + ", userId="
  103. + userId + ", password=" + password + ", nickname=" + nickname
  104. + ", imgUrl=" + imgUrl + ", sex=" + sex + ", age=" + age
  105. + ", create_datetime=" + create_datetime + ", update_datetime="
  106. + update_datetime + ", type=" + type + ", remark=" + remark
  107. + ", visible=" + visible + ", value=" + value + "]";
  108. }
UserMappper.java
  1. public interface UserMapper {
  2. public List<com.gdpost.model.User> findAllUser();
  3. public void insertUser(User user);
  4. }
UserMapper.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.gdpost.mapper.UserMapper">
  6. <!--模板-->
  7. <select id="findAllUser" resultType="com.gdpost.model.User">
  8. select * from user
  9. </select>
  10. <insert id="insertUser" parameterType="com.gdpost.model.User">
  11. insert into user(
  12. id,username,password,nickname,imgUrl,sex,age,create_datetime,update_datetime,type,remark,visible,value,userId
  13. )
  14. values(
  15. #{id},#{username},#{password},#{nickname},#{imgUrl},#{sex},#{age},#{create_datetime},#{update_datetime},#{type},#{remark},#{visible},#{value},#{userId}
  16. )
  17. </insert>
  18. </mapper>
UserService.java
  1. public interface UserService {
  2. public List<User> findAllUser();
  3. public void insertUser();
  4. }
UserServiceImpl.java
  1. @Service("userService")
  2. public class UserServiceImpl implements UserService{
  3. @Autowired
  4. private UserMapper userMapper;
  5. public List<User> findAllUser() {
  6. List<User> users = userMapper.findAllUser();
  7. return users;
  8. }
  9. //事务控制成功
  10. @Transactional
  11. public void insertUser() {
  12. User user1 = new User();
  13. user1.setId(1l);
  14. user1.setUsername("1");
  15. user1.setPassword("1");
  16. user1.setUserId("1");
  17. userMapper.insertUser(user1);
  18. int i=1/0;
  19. User user2 = new User();
  20. user2.setId(2l);
  21. user2.setUsername("2"+i);
  22. user2.setPassword("2");
  23. user2.setUserId("2");
  24. userMapper.insertUser(user2);
  25. }
  26. }

insertUser方法是测试事务,经过测试,事务可以回滚。

8、App.java

  1. public class App {
  2. private static final Logger log = Logger.getLogger(App.class);
  3. public static void main(String[] args) {
  4. UserService userService =(UserService) SpringUtil.getBean("userService");
  5. List<User> users = userService.findAllUser();
  6. log.info("user:"+users);
  7. userService.insertUser();
  8. }
  9. }

这个是启动类,直接使用即可。

GITHUB地址

https://github.com/suibibk/app-template.git

总结

以后可以直接用了,美滋滋

 210

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2