Java で永続層フレームワークを手書きする方法

PHPz
リリース: 2023-04-18 19:37:38
転載
1302 人が閲覧しました

JDBC 操作のレビューと問題分析

Java を学習する学生は jdbc に触れたことがあるはずです。初心者の頃に接した jdbc 操作を復習してみましょう

以下はコードはデータベースに接続してユーザーをクエリします。テーブル情報、ユーザー テーブルのフィールドはそれぞれユーザー ID とユーザー名 username です。

Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        User user = new User();
        try {
            // 加载数据库驱动
            //Class.forName("com.mysql.jdbc.Driver");
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 通过驱动管理类获取数据库链接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mimashi3124");
            // 定义sql语句?表示占位符
            String sql = "select * from user where username = ?";
            // 获取预处理statement
            preparedStatement = connection.prepareStatement(sql);
            // 设置参数,第⼀个参数为sql语句中参数的序号(从1开始),第⼆个参数为设置的参数值
            preparedStatement.setString(1, "盖伦");
            // 向数据库发出sql执⾏查询,查询出结果集
            resultSet = preparedStatement.executeQuery();
            // 遍历查询结果集
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                // 封装User
                user.setId(id);
                user.setUsername(username);
            }
            System.out.println(user);
        } catch (
                Exception e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
ログイン後にコピー

コードを見ると、JDBC を使用してデータベースを操作する場合に次の問題が見つかります。

  • データベース接続の頻繁な作成と解放により、システムが無駄になります。リソースに影響を与えるため、システムのパフォーマンスに影響を与えます。

  • コード内に SQL ステートメントを記述します。コードの保守は容易ではありません。実際のアプリケーションでは SQL が大幅に変更される可能性があります。SQL の変更には Java コードの変更が必要です。

  • PreparedStatement を使用して占有シンボルにパラメータを転送するにはハード コーディングが必要です。SQL ステートメントの where 条件が不確実であり、多かれ少なかれ不確かである可能性があり、コードは次のようにする必要があるためです。 SQL を変更するために変更されたため、システムの保守が困難になります。

  • 結果セットの解析にはハード コーディング (クエリ列名) があります。SQL の変更により解析コードも変更されるため、システムの保守が困難になります。データベース レコードを保存できる場合は、

問題解決のアイデア

  • データベース接続プールを使用して、接続リソースを初期化します。リソースの無駄を避ける

  • SQL ステートメントを XML 構成に抽出します。この種の SQL 変更は、XML ファイルに焦点を当てるだけで済みます。SQL を大量に書き直すことよりも優れたものではありません。 java code.

  • パラメータのハードコーディングの問題を解決するには、リフレクション、イントロスペクションなどのテクノロジを使用して、エンティティをテーブルフィールドに自動的にマップします。

独自の永続層フレームワークを作成します

次に、上記の問題を 1 つずつ解決しましょう

c3p0 をデータベース接続プールに直接使用できます提供されている ComboPooledDataSource を使用できます。

SQL ハードコーディングの問題を解決するには、SQL を XML ファイルに書き込む必要があるため、当然 XML ファイルを定義する必要があります。

SQL だけでは明らかに十分ではありません。結局のところ、SQL ステートメントが意味を持つ前に、まずデータベースに接続する必要があります。したがって、データ構成情報を最初に xml で定義し、次に SQL ステートメントを定義する必要があります。

1. 構成 XML ファイルを定義します

新しい sqlMapConfig.xml を作成し、データ ソース情報を定義し、2 つの SQL ステートメントを追加します (parameterType は SQL 実行パラメーター、resultType はメソッドの戻りエンティティ。

コードは次のとおりです (データベースのバージョンが異なると、異なるドライバー クラスが使用される場合があります):

<configuration>
    <!--数据库连接信息-->
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<!--    <property name="driverClass" value="com.mysql.jdbc.Driver"/>-->
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"/>
    <property name="username" value="root"/>
    <property name="password" value="mimashi3124"/>

	<select id="selectOne" parameterType="org.example.pojo.User"
            resultType="org.example.pojo.User">
        select * from user where id = #{id} and username =#{username}
    </select>

    <select id="selectList" resultType="org.example.pojo.User">
        select * from user
    </select>
</configuration>
ログイン後にコピー

XML ファイル データベース情報が利用可能になり、SQL ステートメント定義も利用できるようになりました。他にどのような問題がありますか?

実際の SQL 操作にはさまざまなテーブルが含まれるため、構造をより明確にして保守しやすくするために、それを改善して各テーブルの SQL ステートメントを別個の XML に配置します。

最適化された XML 構成は次のようになります

sqlMapConfig.xml

<configuration>
    <!--数据库连接信息-->
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
	<!-- <property name="driverClass" value="com.mysql.jdbc.Driver"/>-->
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai"/>
    <property name="username" value="root"/>
    <property name="password" value="mimashi3124"/>
    <!--引⼊sql配置信息-->
	<mapper resource="mapper.xml"></mapper>
</configuration>
ログイン後にコピー

mapper.xml

<mapper namespace="user">
    <select id="selectOne" parameterType="org.example.pojo.User"
            resultType="org.example.pojo.User">
        select * from user where id = #{id} and username =#{username}
    </select>

    <select id="selectList" resultType="org.example.pojo.User">
        select * from user
    </select>
</mapper>
ログイン後にコピー

ちなみに、ビジネス エンティティ User# を定義します。 ##

public class User {
    private int id;
    private String username;
    
    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username=&#39;" + username + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }
}
ログイン後にコピー

2. 設定ファイルを読み込みます

読み込み完了後はストリーム形式で存在するため、操作が難しく、解析して情報を取得する必要があります。そしてそれを保存するエンティティ オブジェクトを作成します。

構成: 基本的なデータベース情報、マップ<一意の識別子、マッパー>一意の識別子: 名前空間「.」を格納します。idMappedStatement: SQL ステートメント、入力パラメータのタイプ、出力パラメータのタイプを格納します

xml 分析用に、 dom4j を使用します

最初に Maven の依存関係を紹介します

コードは次のとおりです (mysql ドライバーのバージョンは、使用される実際の mysql バージョンに応じて調整されます):

<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    </dependencies>
ログイン後にコピー

データベース構成エンティティ構成

public class Configuration {
    //数据源
    private DataSource dataSource;
    //map集合: key:statementId value:MappedStatement
    private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();

    public DataSource getDataSource() {
        return dataSource;
    }

    public Configuration setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        return this;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }

    public Configuration setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
        this.mappedStatementMap = mappedStatementMap;
        return this;
    }
}
ログイン後にコピー

SQL文情報エンティティ

public class MappedStatement {

    //id
    private String id;
    //sql语句
    private String sql;
    //输⼊参数
    private String parameterType;
    //输出参数
    private String resultType;

    public String getId() {
        return id;
    }

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

    public String getSql() {
        return sql;
    }

    public MappedStatement setSql(String sql) {
        this.sql = sql;
        return this;
    }

    public String getParameterType() {
        return parameterType;
    }

    public MappedStatement setParameterType(String parameterType) {
        this.parameterType = parameterType;
        return this;
    }

    public String getResultType() {
        return resultType;
    }

    public MappedStatement setResultType(String resultType) {
        this.resultType = resultType;
        return this;
    }
}
ログイン後にコピー

ちなみに、XMLファイルを読み込むためのResourcesクラスを定義します。stream

public class Resources {
    public static InputStream getResourceAsSteam(String path) {
        return Resources.class.getClassLoader().getResourceAsStream(path);
    }
}
ログイン後にコピー

次のステップは実際の解析です。解析コードが多数あるため、クラスを個別にカプセル化することを検討します 解析の処理

データベース構成情報を解析する XMLConfigBuilder クラスを定義します

public class XMLConfigBuilder {

    private Configuration configuration;


    public XMLConfigBuilder() {
        this.configuration = new Configuration();
    }

    public Configuration parserConfiguration(InputStream inputStream) throws DocumentException, PropertyVetoException, ClassNotFoundException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        List<Element> propertyElements = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element propertyElement : propertyElements) {
            String name = propertyElement.attributeValue("name");
            String value = propertyElement.attributeValue("value");
            properties.setProperty(name,value);
        }
        //解析到数据库配置信息,设置数据源信息
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setPassword(properties.getProperty("password"));
		
        configuration.setDataSource(comboPooledDataSource);

		//将configuration传入XMLMapperBuilder中做sql语句解析。
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(this.configuration);
        List<Element> mapperElements = rootElement.selectNodes("//mapper");
        for (Element mapperElement : mapperElements) {
            String mapperPath = mapperElement.attributeValue("resource");
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(mapperPath);
            xmlMapperBuilder.parse(resourceAsStream);
        }
        return configuration;
    }
}
ログイン後にコピー

データベース構成情報を解析する XMLMapperBuilder クラスを定義します

public class XMLMapperBuilder {

    private Configuration configuration;

    public XMLMapperBuilder(Configuration configuration) {
        this.configuration = configuration;
    }
    public void parse(InputStream inputStream) throws DocumentException,
            ClassNotFoundException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        String namespace = rootElement.attributeValue("namespace");
        List<Element> select = rootElement.selectNodes("select");
        for (Element element : select) { //id的值
            String id = element.attributeValue("id");
            String parameterType = element.attributeValue("parameterType"); //输⼊参数
            String resultType = element.attributeValue("resultType"); //返回参数
            //statementId,后续调用通过statementId,找到对应的sql执行
            String key = namespace + "." + id;
            //sql语句
            String textTrim = element.getTextTrim();
            //封装 mappedStatement
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setResultType(resultType);
            mappedStatement.setSql(textTrim);
            //填充 configuration
            configuration.getMappedStatementMap().put(key, mappedStatement);
        }
    }
}
ログイン後にコピー

これで、構成解析メソッドを呼び出すことによって、構成オブジェクトを取得できるようになります。ただし、実際の使用では、構成情報と SQL ステートメントを提供して、メソッドを呼び出して結果を返すことができることを期待しています。

したがって、データベース操作インターフェイス (クラス) も定義する必要があります

3. SQL 操作インターフェイス SqlSession を定義します

public interface SqlSession {
	//查询多个
    public <E> List<E> selectList(String statementId, Object... param) throws Exception;

	//查询一个
    public <T> T selectOne(String statementId,Object... params) throws Exception;
}
ログイン後にコピー

操作インターフェイス SqlSession の特定の実装を作成します。 StatementId を通じて対応する実行用の SQL 情報を検索します

#コード内の simpleExcutor は、実際のデータベース ステートメントの実行、戻りパラメーターのカプセル化、その他の操作を実行します

#
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private Executor simpleExcutor = new SimpleExecutor();

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... param) throws Exception {
        MappedStatement mappedStatement =
                configuration.getMappedStatementMap().get(statementId);
        List<E> query = simpleExcutor.query(configuration, mappedStatement, param);
        return query;
    }

    @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("返回结果过多");
        }
    }
}
ログイン後にコピー

4. データベース実行ロジックを作成します

Database 操作クラス DefaultSqlSession の selectList メソッドは、simpleExcutor.query() メソッドを呼び出します。

public class SimpleExecutor implements Executor {

    private Connection connection = null;

    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {
        //获取连接
        connection = configuration.getDataSource().getConnection();
        // select * from user where id = #{id} and username = #{username} String sql =
        String sql = mappedStatement.getSql();
        //对sql进⾏处理
        BoundSql boundSql = getBoundSql(sql);
        // 3.获取预处理对象:preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4. 设置参数
        //获取到了参数的全路径
        String parameterType = mappedStatement.getParameterType();
        Class<?> parameterTypeClass = getClassType(parameterType);

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();

            //反射
            Field declaredField = parameterTypeClass.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);

        ArrayList<Object> objects = new ArrayList<>();

        // 6. 封装返回结果集
        while (resultSet.next()){
            Object o =resultTypeClass.newInstance();
            //元数据
            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(o,value);
            }
            objects.add(o);
        }
        return (List<E>) objects;
    }

    @Override
    public void close() throws SQLException {

    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if(parameterType!=null){
            Class<?> aClass = Class.forName(parameterType);
            return aClass;
        }
        return null;

    }

    private BoundSql getBoundSql(String sql) {
        //标记处理类:主要是配合通⽤标记解析器GenericTokenParser类完成对配置⽂件等的解 析⼯作,其中
        //TokenHandler主要完成处理
        ParameterMappingTokenHandler parameterMappingTokenHandler = new
                ParameterMappingTokenHandler();
        //GenericTokenParser :通⽤的标记解析器,完成了代码⽚段中的占位符的解析,然后再根 据给定的
       // 标记处理器(TokenHandler)来进⾏表达式的处理
        //三个参数:分别为openToken (开始标记)、closeToken (结束标记)、handler (标记处 理器)
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}",
                parameterMappingTokenHandler);
        String parse = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings =
                parameterMappingTokenHandler.getParameterMappings();
        return new BoundSql(parse, parameterMappings);
    }
}
ログイン後にコピー

上記のコメントはより詳細であり、プロセスは次のとおりです。

    SQL を取得します。対応する statementId に従って実行されるステートメント、呼び出しパラメーター、戻りパラメーター。
  • SQL プレースホルダーを解析し、呼び出しパラメータを設定します。
  • 解析された入力パラメータ フィールドに従って、リフレクションを通じて対応する値を取得します。 値、設定SQL ステートメントのパラメータ
  • SQL ステートメントを実行し、リフレクションとイントロスペクションを使用し、データベース テーブルとエンティティの間の対応関係に従ってオブジェクト属性の設定を完了し、最後に結果。
  • 上記の手順により、データベースの構成と SQL ステートメントの情報を取得しました。データベース操作クラス SqlSession は定義されていますが、解析構成ファイルはどこにも呼び出されません。

我们还需要一个东西把两者给串起来,这里我们可以使用工厂模式来生成SqlSession

使用工厂模式创建SqlSession

public interface SqlSessionFactory {
    public SqlSession openSession();
}
ログイン後にコピー
public class DefaultSqlSessionFactory implements SqlSessionFactory{

    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}
ログイン後にコピー

同时为了屏蔽构建SqlSessionFactory工厂类时获取Configuration的解析过程,我们可以使用构建者模式来获得一个SqlSessionFactory类。

public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream inputStream) throws PropertyVetoException, DocumentException, ClassNotFoundException {
        XMLConfigBuilder xmlConfigerBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigerBuilder.parserConfiguration(inputStream);
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
        return sqlSessionFactory;
    }
}
ログイン後にコピー

5.调用测试

终于好了,通过以上几个步骤我们现在可以具体调用执行代码了。

 public static void main(String[] args) throws Exception {
        InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsSteam);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(1);
        user.setUsername("盖伦");
        User user2 = sqlSession.selectOne("user.selectOne", user);

        System.out.println(user2);
        List<User> users = sqlSession.selectList("user.selectList");
        for (User user1 : users) {
            System.out.println(user1);
        }
    }
ログイン後にコピー

代码正确执行,输出

Java で永続層フレームワークを手書きする方法

⾃定义框架优化

上述⾃定义框架,解决了JDBC操作数据库带来的⼀些问题:例如频繁创建释放数据库连接,硬编
码,⼿动封装返回结果集等问题,现在我们继续来分析刚刚完成的⾃定义框架代码,有没有什么问题呢?

问题如下:

  • dao的实现类中存在重复的代码,整个操作的过程模板重复(创建sqlsession,调⽤sqlsession⽅ 法,关闭sqlsession)

  • dao的实现类中存在硬编码,调⽤sqlsession的⽅法时,参数statement的id硬编码

我们可以使用代理模式,生成代理对象,在调用之前获取到执行方法的方法名、具体类。这样我们就能获取到statementId。

为SqlSession类新增getMappper方法,获取代理对象

public interface SqlSession {
    public <E> List<E> selectList(String statementId, Object... param) throws Exception;

    public <T> T selectOne(String statementId,Object... params) throws Exception;

    //为Dao接口生成代理实现类
    public <T> T getMapper(Class<?> mapperClass);
}
ログイン後にコピー
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private Executor simpleExcutor = new SimpleExecutor();

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... param) throws Exception {
        MappedStatement mappedStatement =
                configuration.getMappedStatementMap().get(statementId);
        List<E> query = simpleExcutor.query(configuration, mappedStatement, param);
        return query;
    }

    @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("返回结果过多");
        }
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            	// selectOne
                String methodName = method.getName();
                // className:namespace
                String className = method.getDeclaringClass().getName();
                //statementId
                String statementId = className+&#39;.&#39;+methodName;
                Type genericReturnType = method.getGenericReturnType();
                //判断是否实现泛型类型参数化
                if (genericReturnType instanceof ParameterizedType){
                    List<Object> objects = selectList(statementId,args);
                    return objects;
                }
                return selectOne(statementId,args);
            }
        });
        return (T) proxyInstance;
    }
}
ログイン後にコピー

定义业务数据dao接口

public interface IUserDao {
    //查询所有用户
    public List<User> findAll() throws Exception;
    //根据条件进行用户查询
    public User findByCondition(User user) throws Exception;
}
ログイン後にコピー

接下来我们只需获取到代理对象,调用方法即可。

public class Main2 {
    public static void main(String[] args) throws Exception {
        InputStream resourceAsSteam = Resources.getResourceAsSteam("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsSteam);
        SqlSession sqlSession = sqlSessionFactory.openSession();
		//获取到代理对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        List<User> all = userDao.findAll();
        for (User user1 : all) {
            System.out.println(user1);
        }
    }
}
ログイン後にコピー

Java で永続層フレームワークを手書きする方法

以上がJava で永続層フレームワークを手書きする方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート