欢迎使用CSDN-markdown编辑器

Posted adalu1986

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欢迎使用CSDN-markdown编辑器相关的知识,希望对你有一定的参考价值。

spring MVC +Spring + Hibernate + PostgreSQL框架的集成和多租户

本文采用maven搭建spring MVC + Spring + Hibernate的web框架,采用postgreSQL作为数据库,数据源采用dbcp。搭建的思路如下:

  • 1. 创建Maven项目,编写pom.xml文件
  • 2. 引入Spring配置
  • 3. hibernate配置
  • 4. 多租户实现
  • 5. 数据库设计
  • 6. 生成业务entity实体
  • 8. 创建dao层
  • 9. 创建service层
  • 10. 测试spring和hibernate整合
  • 11. 引入Spring MVC
  • 12. 配置web.xml容器
  • 13. 创建控制层controller
  • 14. 创建视图层
  • 15. 部署服务器测试

1.创建Maven项目,编写pom.xml文件

pom文件的内容如下:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.ac.bcc</groupId>
  <artifactId>maque</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>maque Maven Webapp</name>
  <url>http://maven.apache.org</url>

   <!-- 指定maven仓库 -->
  <repositories><!-- 发现依赖和扩展的远程资源库 -->
    <!-- maven自己官方仓库 -->
    <repository>
      <id>maven</id>
      <name>Maven Repository Switchboard</name>
      <layout>default</layout>
      <url>http://repo1.maven.org/maven2</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
    <!-- 阿里巴巴发布版本仓库 -->
    <repository>
      <id>alibaba-opensource</id>
      <name>alibaba-opensource</name>
      <url>http://code.alibabatech.com/mvn/releases/</url>
      <layout>default</layout>
    </repository>
    <!-- 阿里巴巴快照版本仓库 -->
    <repository>
      <id>alibaba-opensource-snapshot</id>
      <name>alibaba-opensource-snapshot</name>
      <url>http://code.alibabatech.com/mvn/snapshots/</url>
      <layout>default</layout>
    </repository>
  </repositories>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.2.1.RELEASE</spring.version>
  </properties>

  <dependencies>
    <!-- junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!-- servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>3.0-alpha-1</version>
      <scope>provided</scope>
    </dependency>

    <!-- log4j -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <!-- jstl -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2.1-b03</version>
      <scope>provided</scope>
    </dependency>

    <!-- 文件上传 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.2.2</version>
    </dependency>

    <!-- fastJson json数组有关的 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.1.26</version>
    </dependency>

    <!-- 使用 Jackson 把 Java 对象转换成 JSON 字串 -->
    <dependency>
      <groupId>org.codehaus.jackson</groupId>
      <artifactId>jackson-mapper-asl</artifactId>
      <version>1.9.11</version>
    </dependency>
    <!-- http://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.7.4</version>
    </dependency>
    <!-- http://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.7.4</version>
    </dependency>


     <!-- aspectjweaver.jar这是Spring AOP所要用到的包 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.7.1</version>
    </dependency>

    <!-- hibernate4 -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.2.5.Final</version>
    </dependency>       
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.2.5.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-ehcache</artifactId>
      <version>4.2.5.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate.javax.persistence</groupId>
      <artifactId>hibernate-jpa-2.0-api</artifactId>
      <version>1.0.1.Final</version>
    </dependency>

    <!-- postgreSQL -->
    <dependency>
        <groupId>postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.1-901-1.jdbc4</version>
    </dependency>

    <!-- spring mvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- spring4 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</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-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>


    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>

  </dependencies>
  <build>
    <finalName>maque</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

2.引入Spring配置

编写配置文件,配置文件放在src/main/resources资源目录下(下同)。
项目所需的配置信息config.properties

#PostgreSQL
jdbc.driver=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5432/exampledb
jdbc.username=postgres
jdbc.password=123abc
validationQuery=SELECT 1

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=true

sessionInfoName=sessionInfo

2.1 Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd
">

  <!-- 引入属性文件 -->
  <context:property-placeholder location="classpath:config.properties" />

  <!-- 自动扫描dao和service包(自动注入) -->
  <context:component-scan base-package="cn.ac.bcc.maque.dao,cn.ac.bcc.maque.service" />

  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"></bean>

</beans>

2.2 引入日志文件配置Log4j

log4j.rootLogger=INFO,A1,R

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.Target=System.out
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%c]%m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender 
log4j.appender.R.File=sshf.log
log4j.appender.R.MaxFileSize=10MB
log4j.appender.R.Threshold=ALL
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n

3. Hibernate配置

Hibernate的配置主要包括:配置数据源dbcp,配置SessionFactory,配置事务管理器,配置事务管理,其spring-hibernate.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
">

  <!-- JNDI(通过Tomcat)方式配置数据源 -->
  <!-- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${jndiName}"></property> </bean> -->


  <!-- 配置数据源 -->
  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="driverClassName" value="${jdbc.driver}" ></property>
  </bean>

  <!-- 配置hibernate session工厂 -->
  <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="hibernateProperties">
      <map>
        <!-- <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> -->
        <entry key="hibernate.dialect" value="${hibernate.dialect}"></entry>
        <entry key="hibernate.show_sql" value="${hibernate.show_sql}"></entry>
        <entry key="hibernate.format_sql" value="${hibernate.format_sql}"></entry>
        <entry key="hibernate.current_session_context_class" value="org.springframework.orm.hibernate4.SpringSessionContext"></entry>


        <!-- hibernate多租户配置 --> 
        <entry key="hibernate.multiTenancy" value="SCHEMA"></entry>
        <entry key="hibernate.tenant_identifier_resolver" value="cn.ac.bcc.maque.dao.impl.TenantIdResolver"></entry>
        <entry key="hibernate.multi_tenant_connection_provider" value="cn.ac.bcc.maque.dao.impl.SchemaBasedMultiTenantConnectionProvider"></entry>

      </map>
    </property>


    <!-- 自动扫描注解方式配置的hibernate类文件 -->
    <property name="packagesToScan">
      <list>
        <value>cn.ac.bcc.maque.model</value>
      </list>
    </property>

    <!-- 
    <property name="mappingDirectoryLocations">
      <list>
        <value>classpath:/cn/ac/bcc/maque/model</value>
      </list>
    </property>
     -->
  </bean>



  <!-- 配置事务管理器 -->
  <bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"></property>
  </bean>

  <!-- 注解方式配置事物 -->
  <tx:annotation-driven transaction-manager="transactionManager" /> 

  <!-- 拦截器方式配置事物 -->
  <!-- 
  <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <tx:method name="add*" />
      <tx:method name="save*" />
      <tx:method name="update*" />
      <tx:method name="modify*" />
      <tx:method name="edit*" />
      <tx:method name="delete*" />
      <tx:method name="remove*" />
      <tx:method name="repair" />
      <tx:method name="deleteAndRepair" />

      <tx:method name="get*" propagation="SUPPORTS" />
      <tx:method name="find*" propagation="SUPPORTS" />
      <tx:method name="load*" propagation="SUPPORTS" />
      <tx:method name="search*" propagation="SUPPORTS" />
      <tx:method name="datagrid*" propagation="SUPPORTS" />

      <tx:method name="*" propagation="SUPPORTS" />
    </tx:attributes>
  </tx:advice>
  <aop:config>
    <aop:pointcut id="transactionPointcut" expression="execution(* com.jesus.sshframework.service..*Impl.*(..))" />
    <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
  </aop:config>
 -->

</beans>

4. 多租户实现

在第3步中,配置了多租户,涉及到了两个类TenantIdResolver和SchemaBasedMultiTenantConnectionProvider:

TenantIdResolver的类内容如下:

package cn.ac.bcc.maque.dao.impl;

import org.hibernate.context.spi.CurrentTenantIdentifierResolver;

import cn.ac.bcc.maque.model.Login;

public class TenantIdResolver implements CurrentTenantIdentifierResolver {

    @Override
    public String resolveCurrentTenantIdentifier() {
        return Login.getTenantId();
    }

    @Override
    public boolean validateExistingCurrentSessions() {
        return true;
    }

}

SchemaBasedMultiTenantConnectionProvider的类内容如下:

package cn.ac.bcc.maque.dao.impl;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.HibernateException;
import org.hibernate.service.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Stoppable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository("schemaBasedMultiTenantConnectionProvider")
public class SchemaBasedMultiTenantConnectionProvider implements MultiTenantConnectionProvider,Stoppable,Configurable {
    /**
     * 
     */
    private static final long serialVersionUID = -5379376698684861045L;

    private final DatasourceConnectionProviderImpl connectionProvider = new DatasourceConnectionProviderImpl();


    public void configure(Map configurationValues) {
        this.connectionProvider.configure(configurationValues);
    }

    public Connection getAnyConnection() throws SQLException {
        return connectionProvider.getConnection();
    }

    public void releaseAnyConnection(Connection connection) throws SQLException {
        connectionProvider.closeConnection(connection);
    }

    public Connection getConnection(String tenantIdentifier) throws SQLException {
        final Connection connection = getAnyConnection();
        try {
//            connection.createStatement().execute("USE " + tenantIdentifier);
            connection.createStatement().execute("set schema ‘" + tenantIdentifier + "‘");
        } catch (SQLException e) {
            throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
        }
        return connection;
    }

    public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
        try {
            connection.createStatement().execute("USE test");
        } catch (SQLException e) {
            throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
        }
        connectionProvider.closeConnection(connection);
    }

    public boolean supportsAggressiveRelease() {
        return this.connectionProvider.supportsAggressiveRelease();
    }



    public void stop() {
        this.connectionProvider.stop();
    }

    public boolean isUnwrappableAs(Class unwrapType) {
        return this.connectionProvider.isUnwrappableAs(unwrapType);
    }

    public <T> T unwrap(Class<T> unwrapType) {
        return this.connectionProvider.unwrap(unwrapType);
    }

}

5.数据库设计

在PostgreSQL中,创建database,名为exampledb;创建schema,名为myschema;在mychema中创建table,创建语句如下:

create database exampledb;
create schema myschema;
set schema ‘myschema‘;
create table user_info(id varchar(200),name varchar(50),signup_date
date);

6. 生成业务entity实体

package cn.ac.bcc.maque.model;
// Generated 2016-5-11 17:35:53 by Hibernate Tools 4.3.1.Final

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * User generated by hbm2java
 */
@Entity
@Table(name="user_info")
public class User implements java.io.Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -5656136547913487597L;

    @Id
    @Column(name="id")
    private String id;
    @Column(name="name")
    private String name;
    @Column(name="signup_date")
    private Date signupDate;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getSignupDate() {
        return this.signupDate;
    }

    public void setSignupDate(Date signupDate) {
        this.signupDate = signupDate;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public boolean equals(Object other) {
        if ((this == other))
            return true;
        if ((other == null))
            return false;
        if (!(other instanceof User))
            return false;
        User castOther = (User) other;

        return ((this.getName() == castOther.getName()) || (this.getName() != null && castOther.getName() != null
                && this.getName().equals(castOther.getName())))
                && ((this.getSignupDate() == castOther.getSignupDate())
                        || (this.getSignupDate() != null && castOther.getSignupDate() != null
                                && this.getSignupDate().equals(castOther.getSignupDate())));
    }

    public int hashCode() {
        int result = 17;

        result = 37 * result + (getName() == null ? 0 : this.getName().hashCode());
        result = 37 * result + (getSignupDate() == null ? 0 : this.getSignupDate().hashCode());
        return result;
    }

}

8. 创建dao层

8.1 Dao接口

Dao层和Service层我们肯定采用的是面相接口编程的思想,所以,我们先定义一个通用的Dao接口,GenericDao.java

package cn.ac.bcc.maque.dao;

import java.io.Serializable;
import java.util.List;

public interface GenericDao <T, PK extends Serializable>{
    T load(PK id);  

    T get(PK id);  

    List<T> findAll();  

    void persist(T entity);  

    PK save(T entity);  

    void saveOrUpdate(T entity);  

    void delete(PK id);  

    void flush();  
}

再定义具体的UserDao.java接口:

package cn.ac.bcc.maque.dao;

import cn.ac.bcc.maque.model.User;

public interface UserDao extends GenericDao<User,String>{

}

8.2 Dao层实现

package cn.ac.bcc.maque.dao.impl;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import cn.ac.bcc.maque.dao.UserDao;
import cn.ac.bcc.maque.model.User;

@Repository("userDao")
@Transactional
public class UserDaoImpl implements UserDao {
    @Autowired
    private SessionFactory sessionFactory;

    private Session currentSession(){
        Session session = sessionFactory.getCurrentSession();
        return session;
    }
    @Override
    public User load(String id) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public User get(String id) {
        return (User) currentSession().get(User.class,id);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<User> findAll() {
        List<User> users = currentSession().createQuery("from User user").list();
        return users;
    }

    @Override
    public void persist(User entity) {
        // TODO Auto-generated method stub

    }

    @Override
    public String save(User entity) {
        return (String) currentSession().save(entity);
    }

    @Override
    public void saveOrUpdate(User entity) {
        // TODO Auto-generated method stub

    }

    @Override
    public void delete(String id) {
        currentSession().delete(currentSession().get(User.class, id));

    }

    @Override
    public void flush() {
        // TODO Auto-generated method stub

    }



}

9. 创建service层

9.1 创建service 接口

package cn.ac.bcc.maque.service;

import java.util.List;

import cn.ac.bcc.maque.model.以上是关于欢迎使用CSDN-markdown编辑器的主要内容,如果未能解决你的问题,请参考以下文章

欢迎使用CSDN-markdown编辑器

欢迎使用CSDN-markdown编辑器

欢迎使用CSDN-markdown编辑器

欢迎使用CSDN-markdown编辑器

欢迎使用CSDN-markdown编辑器

欢迎使用CSDN-markdown编辑器