Spring Boot 1 Spring Boot入門 1.1 Spring Boot特性: 能夠快速創建基於 Spring 的應用程式 能夠直接使用 java main 方法啟動內嵌的 Tomcat 伺服器運行 Spring Boot 程式,不需 要部署 war 包文件 提供約定的 starter ...
下圖是JDBC引起的一系列問題以及解決辦法:
自定義持久層框架設計思路:
使用端(項目):引入自定義持久層框架jar包。
提供兩部分配置信息:1,資料庫配置信息;2,sql配置信息--(sql語句、參數類型、返回值類型)
解決辦法:使用配置文件來提供兩部分配置信息:
<1>sqlMapConfig.xml:放資料庫配置信息;在sqlMapConfig.xml中,其實也可以存放mapper.xml的全路徑,方法getResourceAsSteam()可以一次性全部讀取;
<2>mapper.xml:存放sql配置信息;自定義持久層框架本身(工程):本質上就是對JDBC代碼進行了封裝。
(1)載入配置文件,根據配置文件的路徑,載入配置文件成位元組輸入流,存儲在記憶體中;
創建Resource類
方法:getResourceAsSteam(String path)返回 InputSteam;(2)創建兩個javaBean(容器對象):存放的就是對配置文件解析出來的內容,如下:
Configruation核心配置類:存放sqlMapConfig.xml解析出來的內容;
MappedStatement映射配置類:存放mapper.xml解析出來的內容;(3)解析配置文件,可以採用dom4j對配置文件進行解析;
創建類:SqlSessionFactoryBuilder,方法:build(InputSteam is)
<1>使用dom4j解析配置文件,將解析出來的內容封裝到容器對象中;
<2>創建SqlSessionFactory對象;主要作用就是利用工廠模式生產sqlSession(會話對象)(4)基於開閉原則創建SqlSessionFactory介面及實現類DefaultSqlSessionFactory
<1>生產sqlSession【openSqlSession()】(5)創建SqlSession介面及實現類DefaultSession
定義對資料庫的crud操作:selectList()selectOne()update()delete()(6)創建Exeutor介面及實現類SimpleExeutor實現類,執行的就是JDBC代碼;
query(Configruation,MappedStatement,Object ... params);
創建兩個maven工程IPersistence和IPersistence_test
--IPersistence_test引入IPersistence依賴--
<groupId>com.yun</groupId>
<artifactId>IPersistence_test</artifactId>
<version>1.0-SNAPSHOT</version>
<!--引入自定義持久層框架依賴-->
<dependencies>
<dependency>
<groupId>com.yun</groupId>
<artifactId>IPersistence</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
--IPersistence_test-->sqlMapConfig.xml-- <configuration> <!--資料庫配置信息--> <dataSource> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://xxx.xxx.xx.xxx:xxxx/xxxx"></property> <property name="username" value="xxxx"></property> <property name="password" value="xxxx"></property> </dataSource> <!--存放mapper.xml的全路徑--> <mapper resource="userMapper.xml"></mapper> </configuration>
--IPersistence_test-->userMapper.xml-- <mapper namespace="user"> <!--sql的唯一標識應該是由 namespace.id來組成(statementId)--> <select id="selectList" resultType="com.yun.pojo.User"> select * from user </select> <!--利用反射獲取到user對象的參數--> <select id="selectOne" resultType="com.yun.pojo.User" paramterType="com.yum.pojo.User"> select * from user where id = #{id} and username = #{username} </select> </mapper>
--IPersistence-- @Data public class MappedStatement { //id private Integer id; //返回值類型 private String resultType; //參數值類型 private String paramterType; //sql語句 private String sql; }
--IPersistence-- @Data public class Configuration { private DataSource dataSource; /** * k:statementId * v:封裝好的MappedStatement對象 */ Map<String,MappedStatement> map = new HashMap<>(); }
按照設計思路編寫代碼
--解析配置文件返迴流 public class Resources { /** * 根據配置文件的路徑,將配置文件載入成位元組輸入流,存儲在記憶體中 * @param path * @return */ public static InputStream getResourcesAsSteam(String path){ InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path); return resourceAsStream; } }
@Data public class Configuration { private DataSource dataSource; /** * k:statementId * v:封裝好的MappedStatement對象 */ Map<String,MappedStatement> map = new HashMap<>(); }
@Data public class MappedStatement { //id private String id; //返回值類型 private String resultType; //參數值類型 private String paramterType; //sql語句 private String sql; }
--將解析的流封裝到SqlSessionFactoryBuilder中 public class SqlSessionFactoryBuilder { public SqlSessionFactory build(InputStream is) throws Exception { //1,使用dom4j解析配置文件,將解析出來的內容封裝到Configuration中 XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(); Configuration configuration = xmlConfigBuilder.parseConfig(is); //2,創建sqlSessionFactory對象,工廠類:生產sqlSession繪畫對象 DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration); return defaultSqlSessionFactory; } }
--將sqlMapConfig.xml和userMapper.xml流放入configuration中 public class XmlConfigBuilder { private Configuration configuration; public XmlConfigBuilder() { this.configuration = new Configuration(); } /** * 該方法就是使用dom4j將配置文件解析,封裝Configuration * @param is * @return */ public Configuration parseConfig(InputStream is) throws Exception { Document document = new SAXReader().read(is); //獲取Configuration根對象<Configuration> Element rootElement = document.getRootElement(); //獲取sqlMapConfig.xml裡面的配置信息並且遍歷 List<Element> list = rootElement.selectNodes("//property"); Properties properties = new Properties(); for (Element element : list) { String name = element.attributeValue("name"); String value = element.attributeValue("value"); properties.setProperty(name,value); } //創建 c3p0 連接池 ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); comboPooledDataSource.setDriverClass(properties.getProperty("driverClass")); comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl")); comboPooledDataSource.setUser(properties.getProperty("username")); comboPooledDataSource.setUser(properties.getProperty("password")); configuration.setDataSource(comboPooledDataSource); //mapper.xml解析 步驟:拿到路徑-->載入成位元組輸入流-->dom4j進行解析 List<Element> mapperList = rootElement.selectNodes("//mapper"); for (Element element : mapperList) { String mapperPath = element.attributeValue("resource"); InputStream resourcesAsSteam = Resources.getResourcesAsSteam(mapperPath); XmlMApperBuilder xmlMApperBuilder = new XmlMApperBuilder(configuration); xmlMApperBuilder.prase(resourcesAsSteam); } return configuration; } }
public class XmlMApperBuilder { private Configuration configuration; public XmlMApperBuilder(Configuration configuration) { this.configuration = configuration; } public void prase(InputStream is) throws Exception { Document document = new SAXReader().read(is); Element rootElement = document.getRootElement(); String namespace = rootElement.attributeValue("namespace"); List<Element> list = rootElement.selectNodes("//select"); for (Element element : list) { String id = element.attributeValue("id"); String resultType = element.attributeValue("resultType"); String paramterType = element.attributeValue("paramterType"); String sqlText = element.getTextTrim(); MappedStatement mappedStatement = new MappedStatement(); mappedStatement.setId(id); mappedStatement.setResultType(resultType); mappedStatement.setParamterType(paramterType); mappedStatement.setSql(sqlText); //key值是由 namespace.id來組成 String key = namespace +"."+id; configuration.getMap().put(key,mappedStatement); } } }
--利用工廠模式生產sqlSession public class DefaultSqlSessionFactory implements SqlSessionFactory{ private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } @Override public SqlSession openSession() { return new DefaultSqlSession(configuration); } }
@AllArgsConstructor public class DefaultSqlSession implements SqlSession { private Configuration configuration; @Override public <E> List<E> selectList(String statementId, Object... params) throws Exception { //將要去完成對 SimpleExecutor 里的query方法的調用 SimpleExecutor simpleExecutor = new SimpleExecutor(); MappedStatement mappedStatement = configuration.getMap().get(statementId); List<Object> list = simpleExecutor.query(configuration, mappedStatement, params); return (List<E>) list; } @Override public <T> T selectOne(String statementId, Object... params) throws Exception { List<Object> objects = selectList(statementId, params); if (objects.size() == 1) { return (T) objects.get(0); } else { throw new RuntimeException("查詢結果為空或者返回結果過多"); } } }
--註冊驅動,查詢數據信息 並且封裝返回 public class SimpleExecutor implements Executor { @Override //user public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception{ //1,註冊驅動,獲取連接 Connection connection = configuration.getDataSource().getConnection(); //2,獲取sql select * from user where id = #{id} and username = #{username} //轉換sql select * from user where id = ? and username = ?,轉換過程中,還需要對#{}裡面的值進行存儲解析 String sql = mappedStatement.getSql(); BoundSql boundSql = getBoundSql(sql); //3,獲取預處理對象 PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText()); //4,設置參數 //獲取到參數的全路徑 String paramterType = mappedStatement.getParamterType(); Class<?> paramterTypeClass = getClassType(paramterType); List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList(); for (int i = 0; i < parameterMappingList.size(); i++) { ParameterMapping parameterMapping = parameterMappingList.get(i); String content = parameterMapping.getContent(); //反射根據content獲取到實體對象中的屬性值,再根據屬性值獲取到當前傳過來的參數對象 Field declaredField = paramterTypeClass.getDeclaredField(content); //暴力訪問 declaredField.setAccessible(true); Object o = declaredField.get(params[0]); preparedStatement.setObject(i+1,o); } //5,執行sql ResultSet resultSet = preparedStatement.executeQuery(); //獲取實體對象 String resultType = mappedStatement.getResultType(); Class<?> resultTypeClass = getClassType(resultType); //獲取實體對象的具體實現 Object instance = resultTypeClass.newInstance(); List<Object> objects = new ArrayList<>(); //6,封裝返回結果集 while (resultSet.next()) { //1,取出元數據 ResultSetMetaData metaData = resultSet.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { //獲取欄位名 String columnName = metaData.getColumnName(i); //獲取欄位值 Object value = resultSet.getObject(columnName); //使用反射或者內省根據資料庫表和實體的對應關係,完成封裝 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass); Method writeMethod = propertyDescriptor.getWriteMethod(); writeMethod.invoke(instance,value); } objects.add(instance); } return (List<E>) objects; } /** * 反射獲取實體 * @param paramterType * @return * @throws Exception */ private Class<?> getClassType(String paramterType) throws Exception { if (StringUtils.isNullOrEmpty(paramterType)) { Class<?> aClass = Class.forName(paramterType); return aClass; } return null; } /** * 完成對#{}的解析工作:1,將#{}使用?進行代替;2,解析出#{}裡面的值進行存儲 * @param sql * @return */ private BoundSql getBoundSql(String sql) { //標記處理類:配置標記解析器來完成對占位符的解析處理工作 ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler(); //標記解析器,對占位符的轉換 GenericTokenParser tokenParser = new GenericTokenParser("#{", "}", tokenHandler); //解析出來的sql String parseSql = tokenParser.parse(sql); //#{}裡面的解析出來的參數名稱 List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings(); BoundSql boundSql = new BoundSql(parseSql,parameterMappings); return boundSql; } }
開始測試
public class IPersistenceTest { @Test public void test() throws Exception { //獲取配置文件流 InputStream resourcesAsSteam = Resources.getResourcesAsSteam("sqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourcesAsSteam); SqlSession sqlSession = sqlSessionFactory.openSession(); //調用 User user = new User(); user.setId(1); user.setUsername("張三"); User user2 = sqlSession.selectOne("user.selectOne", user); System.out.println(user2); } }
結果: