学任何东西,做任何事情,都得先有原因,否则没有意义,就比如各种框架存在的意义。现在有一种经常遇到的情况,我们经常需要有一个独立的程序来处理一些业务逻辑,也就是小程序(此小程序非微信小程序)。这种小程序有很多,并且每一个都是独立运行,处理独立的业务逻辑,比如定时处理订单的明细数据,定时推送什么的。这种情况下小程序的要求很简单,可以连接数据库即可。
这里用Java小程序来实现,目的是可以连接数据库,控制事物即可,当然后续连接redis、rocketmq也是可以的。那么当然会想得到用spring控制事物,mybatis来操作数据库,数据库连接池用阿里的druid。
我们通常会遇到一个问题,那就是这种小程序打包成jar包后,进程会把一些配置文件,比如数据库链接,自定义的配置文件都打包到jar包里面,但是开发、测试、生产这些东西都是不同的,岂不是我们每次打包之前都要改动配置文件?这样子也太不科学了吧。
目的
搭建一个java小程序模板(可执行jar),用spring管理事务,mybatis操作数据库,用阿里的连接池druid
右键run将该maven打包成一个可执行jar,包括spring依赖都打包成一个jar
项目结构
数据库链接信息以及自定义的配置文件都放在jar包外面也就是app.properties
1、pom.xml
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gdpost</groupId>
<artifactId>app-template</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<!--Spring框架核心库 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring整合oracle -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>12.1.0.1-atlassian-hosted</version>
</dependency>
<!-- spring整合mysql,和上面不冲突 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<!-- 执行package的phase -->
<phase>package</phase>
<!-- 为这个phase绑定goal -->
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!-- 过滤掉以下文件,不打包 :解决包重复引用导致的打包错误-->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<!-- 打成可执行的jar包 的主方法入口-->
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.gdpost.App</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
</build>
</project>
这里用的是最新版的spring,以及用mybatis来操作数据库,要注意的是build中的内容,这里使用maven-shade-plugin插件将项目打成可执行的jar包,因为有第三方依赖包spring所以用这种方法可以把所有jar打包成一个可执行jar,当然用其他插件也是可以的。打包方式是maven-build 然后输入pacakege即可。
2、spring-mybatis.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 自动扫描 -->
<context:component-scan base-package="com.gdpost" />
<!-- 引入配置文件:不需要 -->
<!-- <bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties" />
</bean> -->
<!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean> -->
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring会自动查找其下的类 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gdpost.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--支持注解驱动的事务管理,指定事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
这个配置文件是最重要的配置文件,上面的内容都是必须的,我这里注释掉了数据库连接池对象的生成,因为我把数据库链接的配置信息移动到了外部,所以我这里直接用配置类来生成,如下:
3、DruidDataSourceConfiguration.java
@Configuration
public class DruidDataSourceConfiguration {
private String dbUrl = PropertiesUtil.get("dataSource.url");
private String username =PropertiesUtil.get("dataSource.username");
private String password =PropertiesUtil.get("dataSource.password");
private String driverClassName =PropertiesUtil.get("dataSource.driver");
private int initialSize=Integer.parseInt(PropertiesUtil.get("dataSource.initialSize","5"));
private int minIdle= Integer.parseInt(PropertiesUtil.get("dataSource.minIdle","5"));
private int maxActive= Integer.parseInt(PropertiesUtil.get("dataSource.maxActive","20"));
private int maxWait= Integer.parseInt(PropertiesUtil.get("dataSource.maxWait","60000"));
private int timeBetweenEvictionRunsMillis= Integer.parseInt(PropertiesUtil.get("dataSource.timeBetweenEvictionRunsMillis","60000"));
private int minEvictableIdleTimeMillis= Integer.parseInt(PropertiesUtil.get("dataSource.minEvictableIdleTimeMillis","300000"));
private String validationQuery= PropertiesUtil.get("dataSource.validationQuery","SELECT 1 FROM DUAL");
private boolean testWhileIdle= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testWhileIdle","true"));
private boolean testOnBorrow= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testOnBorrow","false"));
private boolean testOnReturn= Boolean.parseBoolean(PropertiesUtil.get("dataSource.testOnReturn","false"));
private boolean poolPreparedStatements=Boolean.parseBoolean(PropertiesUtil.get("dataSource.poolPreparedStatements","true"));
private int maxPoolPreparedStatementPerConnectionSize= Integer.parseInt(PropertiesUtil.get("dataSource.maxPoolPreparedStatementPerConnectionSize","20"));
//wall, 去掉这个,不然会报sql injection violation
private String filters= PropertiesUtil.get("dataSource.filters","stat,log4j");
private String connectionProperties= PropertiesUtil.get("dataSource.connectionProperties","druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");
@Bean //声明其为Bean实例
@Primary //在同样的DataSource中,首先使用被标注的DataSource
public DataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
datasource.setConnectionProperties(connectionProperties);
return datasource;
}
}
@Configuration 表示这个java类是一个配置类,下面的dataSource就对应配置文件中注释掉的dataSource,这样子就不用从classpath下面获取链接信息啦,直接通过配置文件工具类PropertiesUtil从app.properties中获取,下面我们来看一下配置文件工具类怎么实现的。
4、PropertiesUtil.java
/**
* 用完美的单例模式获取properties的数据,加载为map
* @author forever
*
*/
public class PropertiesUtil {
//值可变,引用不可变
private static final Properties PROPERTIES =new Properties();
static{
initProperties();
}
/**
* @param key
* @param defaultValue
* @return
*/
public static String get(String key,String defaultValue){
if(PROPERTIES.isEmpty()) {
throw new UnsupportedOperationException("配置未加载");
}
String value =PROPERTIES.getProperty(key, defaultValue);
return value;
}
/**
* 获取默认配置的值,这个值可以动态修改
* @param key
* @return
*/
public static String get(String key){
return get(key, "");
}
private static void initProperties(){
InputStream is =null;
try {
//获取当前目录
String property = System.getProperty("user.dir");
//默认是linux os
String fileName = "/app.properties";
//判断是否是windows os
if(System.getProperty ("os.name").contains("Windows")) {
fileName = "\\app.properties";
}
// 读取当前目录下conf配置文件
File file = new File(property+fileName);
PROPERTIES.clear();
is = new FileInputStream(file);
PROPERTIES.load(is);
}catch (IOException e) {
e.printStackTrace();
}finally {
if(is!=null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
上面巧妙的用了这个方法
String property = System.getProperty("user.dir");
来获取当前目录,这样子配置文件就跟jar包分开来啦,完美,并且在static静态代码块中来初始化配置类,这样子在第一次使用的时候就可以初始化了,然后我们来看一下我们的配置类:
5、app.properties
###数据库链接
dataSource.driver=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/weixinser
dataSource.username=root
dataSource.password=forever
###连接池配置信息(有默认配置)
#dataSource.initialSize=5
#dataSource.minIdle=5
#dataSource.maxActive=20
#dataSource.maxWait=60000
#dataSource.timeBetweenEvictionRunsMillis=60000
#dataSource.minEvictableIdleTimeMillis=300000
#dataSource.validationQuery=SELECT 1 FROM DUAL
#dataSource.testWhileIdle=true
#dataSource.testOnBorrow=false
#dataSource.testOnReturn=false
#dataSource.poolPreparedStatements=true
#dataSource.maxPoolPreparedStatementPerConnectionSize=20
###wall, 去掉这个,不然会报sql injection violation
#dataSource.filters=stat,log4j
#dataSource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
###下面是自定义配置
username=lwh
上面的数据库链接只要换成oracle就对应oracle的数据库,并且链接池信息有默认的,修改的话就放开对应的注释即可。然后自定义配置就写在后面,获取配置信息的方法就是PropertiesUtil.get("username");
我们再看下我们的最重要的SpringUtil
6、SpringUtil.java
public class SpringUtil {
private static ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");
public static Object getBean(String serviceName){
return context.getBean(serviceName);
}
}
这里是在开始用的时候就初始化了spring容器,然后就直接使用啦。
后面我们再列一下业务逻辑要使用的类:
7、User.java、UserMappper.java、UserMapper.xml、UserService.java、UserServiceImpl.java
User.java
public class User{
private Long id;//自增的ID
private String username;//用户名
private String userId;//这个用来存放用户的userId,不要用自增键太不安全
private String password;//用户密码
private String nickname;//昵称
private String imgUrl;//用户头像
private String sex;//用户性别0女,1是男
private String age;//用户年龄
private String create_datetime;//用户创建时间
private String update_datetime;//用户修改时间
private String type;//用户类别0是管理员,1是已登录用户,2是游客
private String remark;//用户备注
private String visible;//用户是否有效
private String value;//预留字段
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getCreate_datetime() {
return create_datetime;
}
public void setCreate_datetime(String createDatetime) {
create_datetime = createDatetime;
}
public String getUpdate_datetime() {
return update_datetime;
}
public void setUpdate_datetime(String updateDatetime) {
update_datetime = updateDatetime;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getVisible() {
return visible;
}
public void setVisible(String visible) {
this.visible = visible;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", userId="
+ userId + ", password=" + password + ", nickname=" + nickname
+ ", imgUrl=" + imgUrl + ", sex=" + sex + ", age=" + age
+ ", create_datetime=" + create_datetime + ", update_datetime="
+ update_datetime + ", type=" + type + ", remark=" + remark
+ ", visible=" + visible + ", value=" + value + "]";
}
UserMappper.java
public interface UserMapper {
public List<com.gdpost.model.User> findAllUser();
public void insertUser(User user);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gdpost.mapper.UserMapper">
<!--模板-->
<select id="findAllUser" resultType="com.gdpost.model.User">
select * from user
</select>
<insert id="insertUser" parameterType="com.gdpost.model.User">
insert into user(
id,username,password,nickname,imgUrl,sex,age,create_datetime,update_datetime,type,remark,visible,value,userId
)
values(
#{id},#{username},#{password},#{nickname},#{imgUrl},#{sex},#{age},#{create_datetime},#{update_datetime},#{type},#{remark},#{visible},#{value},#{userId}
)
</insert>
</mapper>
UserService.java
public interface UserService {
public List<User> findAllUser();
public void insertUser();
}
UserServiceImpl.java
@Service("userService")
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
public List<User> findAllUser() {
List<User> users = userMapper.findAllUser();
return users;
}
//事务控制成功
@Transactional
public void insertUser() {
User user1 = new User();
user1.setId(1l);
user1.setUsername("1");
user1.setPassword("1");
user1.setUserId("1");
userMapper.insertUser(user1);
int i=1/0;
User user2 = new User();
user2.setId(2l);
user2.setUsername("2"+i);
user2.setPassword("2");
user2.setUserId("2");
userMapper.insertUser(user2);
}
}
insertUser方法是测试事务,经过测试,事务可以回滚。
8、App.java
public class App {
private static final Logger log = Logger.getLogger(App.class);
public static void main(String[] args) {
UserService userService =(UserService) SpringUtil.getBean("userService");
List<User> users = userService.findAllUser();
log.info("user:"+users);
userService.insertUser();
}
}
这个是启动类,直接使用即可。
GITHUB地址
https://github.com/suibibk/app-template.git
总结
以后可以直接用了,美滋滋