精华内容
下载资源
问答
  • Dao层接口实现优化

    千次阅读 2018-09-27 16:16:49
    Dao层接口实现优化(针对CRUD) 如果一个项目中有多个实体类(domian包下面有多个类,例如学生,老师,用户,部门等),那么对应的dao下面也会有多个对应的接口和多个实现类。考虑如何优化抽取dao层对应的接口和实现...

    Dao层接口实现优化(针对CRUD)

    如果一个项目中有多个实体类(domian包下面有多个类,例如学生,老师,用户,部门等),那么对应的dao下面也会有多个对应的接口和多个实现类。考虑如何优化抽取dao层对应的接口和实现类。

    Dao

    DAO(Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道。夹在业务逻辑与数据库资源中间。
    在核心J2EE模式中是这样介绍DAO模式的:为了建立一个健壮的J2EE应用,应该将所有对数据源的访问操作抽象封装在一个公共API中。用程序设计的语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口在逻辑上对应这个特定的数据存储。


    那么在实际开发中就一定会存在多个实体类,每个实体类都会有操作,如果为每一个实体类分别创建实现,可以,但是会有比较大的冗余,所以这时便需要对整个dao层结构进行重构优化。

    思路:
    每个实体类,例如老师、学生…等等,都会存在CRUD操作,我们可以创建一个BaseDao将这些操作抽取出来,创建对应的实现类。再创建一个工具类提供连接数据库、释放连接操作。
    期望:
    能够简化SQL操作,不用每次操作数据库都需要输入繁杂的sql语句,可以实现传入一个对象即可以将对象存入到数据库
    项目结构

    具体实现CRUD

    1. 首先需要创建工具类提供连接、释放连接
    2. 其次定义基类Dao,定义CRUD方法
    3. 创建实现类实现基类Dao接口
      大体思路:
      1)通过定义泛型方法使得能够接受所有类型,并且保证传入类型在方法整个使用过程中始终为传入类型。
      2)通过获取传入对象字节码对象获取到该对象的所有字段以拼接成为SQL语句中的列值,形如 Insert into Student(字段1,字段2,…)。字段拼接则是实现括号内的所有内容的拼接。接下来我们还需要拼接出SQL后半部分的语句,这里采用的是PreparedStatement来实现SQL的执行。preparedStatement将会在本文末尾详细介绍。
      使用PreparedStatement的好处在于不用拼接值,安全性、效率高于Statement
      所以,后半句需要拼接对应字段个数的占位符。再为占位符赋值便可以实现SQL语句执行
    4. 进行CRUD操作

    建立工具类

    1. 首先需要建立资源文件夹,里面配置好数据库连接信息
    2. 使用连接池获取连接
      最重要的是需要导包。需要的包有:commons-dbcp-1.4.jar 用于连接池操作, commons-pool-1.5.6.jar连接池包, mysql-connector-java-5.1.26-bin.jar用于连接数据库
      代码:
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp.BasicDataSource;
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    
    /**
     * 获取连接类
     * @author xer
     *
     */
    public enum JDBCUtil {
    	/**
    	 * 连接池
    	 * 	不直接通过DriverManager获取连接,而是通过在一个“池子”中创建多个连接,
    	 * 	每次都到池子里去取出空闲连接,进行操作,操作完成之后释放连接,放回连接池
    	 * 		
    	 * 采取方式二连接池操作
    	 */
    	instance;
    	//要在静态代码块中使用properties对象,所以需要static修饰,但是不希望外部能用到此对象所以private修饰
    	private static Properties pro = new Properties();
    	private static DataSource bds = new BasicDataSource();
    	static {
    		try {
    			//通过当前线程获取到类加载器将数据以流的形式读入
    			
    			//获取连接池方式1
    			//读取资源文件中数据库配置信息,注意采用方式二时资源文件中的内容名称要规范定义
    			pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
    			/*bds.setDriverClassName(pro.getProperty("driverClassName"));
    			bds.setUrl(pro.getProperty("url"));
    			bds.setUsername(pro.getProperty("username"));
    			bds.setPassword(pro.getProperty("password"));*/
    			
    			
    			//获取连接池方式2
    			bds = BasicDataSourceFactory.createDataSource(pro);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	public Connection getConnection() {
    		try {
    			return bds.getConnection();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    	public void closeConnection(Connection conn,ResultSet rs,Statement cs) {
    		
    			try {
    				if(rs != null) rs.close();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}finally {
    				try {
    					if(cs != null)cs.close();
    				} catch (SQLException e) {
    					e.printStackTrace();
    				}finally {
    					try {
    						if(conn != null)conn.close();
    					} catch (SQLException e) {
    						e.printStackTrace();
    					}
    				}
    			}
    	}
    }
    

    基类Dao

    import java.util.List;
    
    /**
     * 		各种实体类dao接口的父类接口
     * 		因为每一种实体类dao的操作无非就是CRUD,所以将这些抽取出来提供一个泛型父类接口
     * 		子类dao接口便可以继承实现baseDao指定类型的操作
     * @author xer
     *
     * @param <T> 指定dao的类型
     */
    public interface BaseDao<T> {
    	void add(T obj);
    	int del(T obj);
    	void update(T obj);
    	T query(T obj);
    	List<T> queryAll(T obj);
    }
    
    

    定义实现类

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import cn.itsource.dao.BaseDao;
    import cn.itsource.util.JDBCUtil;
    
    public class DaoImpl<T> implements BaseDao<T>{
    	/**
    		 * 添加
    		 *		添加数据
    		 *			1.首先方法为泛型方法,传入对象为什么类型则T就是什么类型
    		 *			2.通过Class获取到该对象字节码对象方便后面获取该对象所有字段做准备
    		 *			3.通过拼接方法将获取到的所有字段拼接成SQL语句,并且拼接相应个数的问号
    		 *				形如:INSERT INTO table(field1,field2.....) 
    		 *					 VALUES(?,?,?...)
    		 *			4.使用preparedStatement为相应问号赋值
    		 *			5.执行SQL得到结果返回,关闭连接
    		 * 
    		 */
    		@Override
    		public void add(T obj) {
    			PreparedStatement ps = null;
    			Connection conn = null;
    			try {
    				//获取传入对象的字节码对象
    				Class<? extends Object> clazz = obj.getClass();
    				//获得所有字段,因为我们使用的实体类多半都是private修饰的所以要使用getDeclaredFields
    				 Field[] fields = clazz.getDeclaredFields();
    				 //字段,用于拼接SQL语句使用
    				StringBuffer field = new StringBuffer();
    				//问号,用于拼接SQL
    				StringBuffer sb3 = new StringBuffer();
    				for (int i = 1; i< fields.length; i++) {
    					//因为要涉及到对私有字段的进行操作,所以需要设置权限
    					fields[i].setAccessible(true);
    					//拼接字符串
    					if(i != fields.length-1) {
    						field = field.append(fields[i].getName()+",");
    					}else {
    						field = field.append(fields[i].getName());
    					}
    				}
    				//拼接问号
    				for (int i = 1; i < fields.length; i++) {
    					if(i != fields.length-1) {
    						sb3 = sb3.append("?,");
    					}else {
    						sb3 = sb3.append("?");
    					}
    				}
    				
    				String sql = "insert into "+clazz.getSimpleName()+"("+field+") values("+sb3+")";
    				System.out.println(sql);
    				//获取连接
    				conn = JDBCUtil.instance.getConnection();
    				//预编译SQL
    				ps = conn.prepareStatement(sql);
    				for (int i = 1; i < fields.length; i++) {
    					ps.setObject(i, fields[i].get(obj));
    				}
    				ps.executeUpdate();
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}finally {
    				//关闭释放连接
    				JDBCUtil.instance.closeConnection(conn, null, ps);
    			}
    		}
    		
    		/**
    		 * 删除
    		 * 		思路:
    		 * 			删除之前先对传入对象进行查询,查询根据id
    		 * 				如果不存在则直接返回0
    		 * 				如果存在则继续进行,其后进行的步骤与上面基本一致
    		 */
    		@Override
    		public int del(T obj) {
    			//判断查询结果
    			if (query(obj) == null) {
    				return 0;
    			}
    			
    			PreparedStatement ps = null;
    			Connection conn = null;
    			int result = 0;
    			try {
    				Class<? extends Object> clazz = obj.getClass();
    				Field[] fields = clazz.getDeclaredFields();
    				//设置权限
    				for (int i = 0; i< fields.length; i++) {
    					fields[i].setAccessible(true);
    				}
    				String sql = "delete from "+clazz.getSimpleName()+" where id = "+fields[0].get(obj)+"";
    				System.out.println(sql);
    				//获取连接
    				conn = JDBCUtil.instance.getConnection();
    				//预编译SQL
    				ps = conn.prepareStatement(sql);
    				result = ps.executeUpdate();
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}finally {
    				JDBCUtil.instance.closeConnection(conn, null, ps);
    			}
    			return result;
    		}
    		/**
    		 * 更新数据
    		 * 		思路:
    		 * 			模拟前端返回的数据是 一整条数据:即无论用户修改的是一行数据中的哪个字段,
    		 * 			我们都将那一整行数据返回,封装成一个对象传入update方法
    		 * 			根据传入对象,直接修改数据库中对应对象的数据
    		 */
    		@Override
    		public void update(T obj) {
    			PreparedStatement ps = null;
    			Connection conn = null;
    			try {
    				Class<? extends Object> clazz = obj.getClass();
    				 Field[] fields = clazz.getDeclaredFields();
    				 //字段
    				StringBuffer field = new StringBuffer();
    				//获取字段名拼接sql
    				
    				fields[0].setAccessible(true);
    				//拼接SQL语句,因为直接略过了id字段,所以单独设置了id的权限
    				for (int i = 1; i< fields.length; i++) {
    					//设置权限,不包含id
    					fields[i].setAccessible(true);
    					if(i != fields.length-1) {
    						field = field.append(fields[i].getName()+" = ?,");
    					}else {
    						field = field.append(fields[i].getName()+" = ?");
    					}
    				}
    				
    				String sql = "UPDATE "+clazz.getSimpleName()+" SET "+field+" where id = "+fields[0].get(obj)+"";
    				System.out.println(sql);
    				//获取连接
    				conn = JDBCUtil.instance.getConnection();
    				//预编译SQL
    				ps = conn.prepareStatement(sql);
    				for (int i = 1; i < fields.length; i++) {
    					ps.setObject(i, fields[i].get(obj));
    				}
    				ps.executeUpdate();
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}finally {
    				JDBCUtil.instance.closeConnection(conn, null, ps);
    			}
    			
    		}
    		/**
    		 * 查询
    		 * 		根据传入对象的字节码对象获取到id字段
    		 * 		根据id字段获取数据(此处默认id字段在表中第一列,也实体类中第一个)
    		 */
    		@Override
    		public T query(T obj) {
    			PreparedStatement ps = null;
    			Connection conn = null;
    			ResultSet rs = null;
    			try {
    				Class<? extends Object> clazz = obj.getClass();
    				Field[] fields = clazz.getDeclaredFields();
    				for (int i = 0; i < fields.length; i++) {
    					fields[i].setAccessible(true);
    				}
    				String sql = "SELECT * from "+clazz.getSimpleName()+" where id = "+fields[0].get(obj)+"";
    				System.out.println(sql);
    				//获取连接
    				conn = JDBCUtil.instance.getConnection();
    				//预编译SQL
    				ps = conn.prepareStatement(sql);
    				rs = ps.executeQuery(sql);
    				while (rs.next()) { 
    					for (int i = 0; i < fields.length; i++) {
    						fields[i].set(obj, rs.getObject(i+1));
    					}
    				}
    				
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}finally {
    				JDBCUtil.instance.closeConnection(conn, rs, ps);
    			}
    			return obj;
    		}
    
    		@Override
    		/**
    		 * 查询全部
    		 * 		
    		 */
    		public List<T> queryAll(T obj) {
    			PreparedStatement ps = null;
    			Connection conn = null;
    			ResultSet rs = null;
    			List<T> list = new ArrayList<>();
    			T o = null;
    			try {
    				Class<? extends Object> clazz = obj.getClass();
    				Field[] fields = clazz.getDeclaredFields();
    				for (int i = 0; i < fields.length; i++) {
    					fields[i].setAccessible(true);
    				}
    				String sql = "SELECT * from "+clazz.getSimpleName()+"";
    				System.out.println(sql);
    				//获取连接
    				conn = JDBCUtil.instance.getConnection();
    				//预编译SQL
    				ps = conn.prepareStatement(sql);
    				rs = ps.executeQuery(sql);
    				while (rs.next()) { 
    					//创建用于封装接收每一组数据的对象
    					o = (T) clazz.getConstructor().newInstance();
    					for (int i = 0; i < fields.length; i++) {
    						fields[i].set(o, rs.getObject(i+1));
    					}
    					//将对象封装到list中
    					list.add((T) o);
    				}
    				
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (IllegalArgumentException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			} catch (InstantiationException e) {
    				e.printStackTrace();
    			} catch (InvocationTargetException e) {
    				e.printStackTrace();
    			} catch (NoSuchMethodException e) {
    				e.printStackTrace();
    			}finally {
    				JDBCUtil.instance.closeConnection(conn, rs, ps);
    			}
    			return list;
    		}
    		
    }
    

    测试类

    import java.util.List;
    
    import org.junit.Test;
    
    import cn.itsource.dao.impl.DaoImpl;
    import cn.itsource.domain.Student;
    import cn.itsource.domain.Teacher;
    public class DaoTest {
    	DaoImpl<Object> dao = new DaoImpl<Object>();
    	@Test
    	public void testAdd() {
    //		Student s = new Student("瘦狗","123456",20,(byte)1,"世界和平");
    		Teacher t = new Teacher("骚猪",22,"男");
    //		dao.add(s);
    		dao.add(t);
    	}
    
    	@Test
    	public void testDel() {
    		Student s = new Student();
    		s.setId(5);
    		int del = dao.del(s);
    		System.out.println("删除"+del+"行");
    	}
    
    	@Test
    	public void testUpdate() {
    		Student s = new Student(6,"fdg","12456",21,true,"fds");
    		dao.update(s);
    		
    	}
    
    	@Test
    	public void testQueryOne() {
    		Student s = new Student();
    		s.setId(5);
    		System.out.println(dao.query(s));
    	}
    	@Test
    	public void testQueryAll() {
    		/*Student s = new Student();
    		List<Object> queryAll = dao.queryAll(s);
    		for (Object object : queryAll) {
    			System.out.println(object);
    		}*/
    		Teacher teacher = new Teacher();
    		List<Object> queryAll2 = dao.queryAll(teacher);
    		for (Object object : queryAll2) {
    			System.out.println(object);
    		}
    	}
    }
    

    PreparedStatement

    Statement: 表示静态SQL语句对象.
    PreparedStatement:Statement的子接口,表示预编译SQL语句对象.

    什么是预编译SQL语句

    预编译语句PreparedStatement 是java.sql中的一个接口,它是Statement的子接口。通过Statement对象执行SQL语句时,需要将SQL语句发送给DBMS,由 DBMS首先进行编译后再执行。预编译语句和Statement不同,在创建PreparedStatement 对象时就指定了SQL语句,该语句立即发送给DBMS进行编译。当该编译语句被执行时,DBMS直接运行编译后的SQL语句,而不需要像其他SQL语句那样首先将其编译,再执行。
    API示例:
    API示例

    上面语句中的问号便是占位符,即使用其把位置占住,之后我们在为其设置值,这样很好的避免了SQL注入问题

    展开全文
  • 公共DAO层接口与实现

    2008-07-01 21:43:19
    公共DAO层接口与实现 1、dao层接口 2、Hibernate实现 3、Spring JDBC的面向对象实现方式
  • 团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是了个实现类。于是遇到错误了。找不到那个方法。问了...

    转载请注明出处:http://blog.csdn.net/qq_26525215

    本文源自大学之旅_谙忆的博客

    团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是写了个实现类。于是遇到错误了。

    找不到那个方法。问了团队的人才知道,方法名和Mapper中配置的id名必须一样。

    实现:

    一、配置Spring集成MyBatis:

    <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"...
    
    -------------------------------------------------
    
    <!-- 配置数据源 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        ...
    -------------------------------------------------
    
    <!-- 产生sqlsessionfactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml" /> 
    </bean>  
    
    --------------------------------------------------

    要实现对数据库的操作必须要有sqlSession,而sqlSession是由sqlSessionFactory创建的。我们可以在Spring配置好bean。

    <!-- 自动扫描mapper接口-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"
                p:basePackage="com.xiaojuzi.chaojijuhui.**.dao"
                p:sqlSessionFactoryBeanName="sqlSessionFactory" />

    这个配置就是配置映射文件的路径,这样做的好处就是不用再写Dao的实现类了,也就是说,我们写好接口,写好配置文件,会自动映射到对应的方法和sql语句。

    二、开发mapper.xml映射文件

    <mapper namespace="com.xiaojuzi.chaojijuhui.user.dao.UserDao">
    
    -------------------------------------------------------

    在这里只有一个UserDao(被代理的接口)。
    user.mapper.xml–namespace配置的就是UserDao的包全名。

    三、开发mapper.java的接口

         /**
         * 根据用户的用户名查询用户
         * @param user
         * @return
         */
        User queryUserByLoginName (String loginName);
    
        /**
         * 用户通过手机号码去修改密码
         * @param userModel
         * @return
         */
        Boolean updatePasswordByMobile(UserModel userModel);
    

    如果需要特定类型的参数,就自己再造一个POJO类(例如:UserModel)。

     <sql id="userColumns">
            u.id,
            u.login_name as "loginName",
            u.head_img as "headImg",
            ...
    ---------------------------------------------
    
    <select id="queryUserByLoginName" resultType="User" parameterType="User">
            SELECT <include refid="userColumns" />
            FROM juhui_user u
            WHERE u.login_name = #{loginName}
            and u.del_flag = #{DEL_FLAG_NORMAL}
        </select>
    
    <update id="updatePasswordByMobile" parameterType="UserModel">
            update juhui_user set
                update_date=DATE_FORMAT(#{updateDate}, '%Y-%m-%d %H:%i:%S'),
                salt = #{salt},
                password = #{password}
            where mobile = #{mobile}
        </update>

    这里mapper.xml的(select、insert、update..)标签的id必须和DAO接口的方法名一样!

    Mapper开发规则

    1、 在mapper.xml中将namespace设置为mapper.java的全限定名
    2、 将mapper.java接口的方法名和mapper.xml中statement的id保持一致。
    3、 将mapper.java接口的方法输入参数类型和mapper.xml中statement的parameterType保持一致
    4、 将mapper.java接口的方法输出 结果类型和mapper.xml中statement的resultType保持一致

    注意遵循上边四点规范!

    这样抛弃Dao实现类的写法:
    具有更好的可扩展性,提高了灵活度。

    原理

    再根据网上的一些知识点,讲一下原理:

    mybatis通过JDK的动态代理方式,在启动加载配置文件时,根据配置mapper的xml去生成Dao的实现。

    session.getMapper()使用了代理,当调用一次此方法,都会产生一个代理class的instance,看看这个代理class的实现.

    public class MapperProxy implements InvocationHandler { 
    ... 
    public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) { 
        ClassLoader classLoader = mapperInterface.getClassLoader(); 
        Class<?>[] interfaces = new Class[]{mapperInterface}; 
        MapperProxy proxy = new MapperProxy(sqlSession); 
        return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); 
      } 
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        if (!OBJECT_METHODS.contains(method.getName())) { 
          final Class<?> declaringInterface = findDeclaringInterface(proxy, method); 
          final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession); 
          final Object result = mapperMethod.execute(args); 
          if (result == null && method.getReturnType().isPrimitive()) { 
            throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" +    method.getReturnType() + ")."); 
          } 
          return result; 
        } 
        return null; 
      } 

    这里是用到了JDK的代理Proxy。 newMapperProxy()可以取得实现interfaces 的class的代理类的实例。

    当执行interfaces中的方法的时候,会自动执行invoke()方法,其中public Object invoke(Object proxy, Method method, Object[] args)中 method参数就代表你要执行的方法.

    MapperMethod类会使用method方法的methodName 和declaringInterface去取 sqlMapxml 取得对应的sql,也就是拿declaringInterface的类全名加上 sql-id..

    总结:
    这个就是利用JDK的代理类实现的。

    本文章由[谙忆]编写, 所有权利保留。

    转载请注明出处:http://blog.csdn.net/qq_26525215

    本文源自大学之旅_谙忆的博客

    展开全文
  • 一、Dao层接口设计理论 设计图: 传统的Dao层: UserDao 接口中定义 增删改查方法 UserDaoImpl 类实现这些 增删改查方法 RoleDao 接口中也定义 增删改查方法 RoleDaoImpl 类实现这些 增删改查方法 N 个DAO ...

    一、Dao层接口设计理论

    设计图:

    传统的Dao层:

    UserDao 接口中定义 增删改查方法

    UserDaoImpl 类实现这些 增删改查方法

    RoleDao 接口中也定义 增删改查方法

    RoleDaoImpl  类实现这些 增删改查方法

    N 个DAO 接口就要定义 N 个增删改查方法,还要有 N 个类中实现这些增删改查方法。

    问题来了, 这些在接口中定义重复的方法造成代码的冗余。

     

    解决办法:

    ①、 定义一个 通用的 Dao 接口(如: BaseDao 接口),里面定义 对数据的增删改查方法。

    ②、所有的 Dao 接口继承 BaseDao 接口,即无需再每个 Dao 接口中定义 增删改查方法

    ③、定义一个 通用的 Dao 接口实现类(如:BaseDaoImpl 实现类),实现 通用的增删改查方法。

    ④、所有的 Dao 接口实现类 继承 BaseDaoImpl 实现类,即 无需再每个 接口实现类中 实现 增删改查方法

     


    二、Dao层接口代码

    1)通用的 Dao 接口(BaseDao),代码如下:

    package cn.oa.base;
    
    import java.util.List;
    
    public interface BaseDao<T> {
    	
    	void save(T t);
    	
    	void delete(Long id);
    	
    	void update(T t);
    	
    	T getById(Long id);
    	
    	List<T> getByIds(Long[] ids);
    	
    	List<T> findAll();
    
    }

    2)所有的 Dao 接口继承 BaseDao, 代码如下:

    UserDao 接口

    package cn.oa.dao;
    
    import cn.oa.base.BaseDao;
    import cn.oa.model.User;
    
    public interface UserDao extends BaseDao<User>{
    
    }
    

    RoleDao 接口

    package cn.oa.dao;
    
    import cn.oa.base.BaseDao;
    import cn.oa.model.Role;
    
    public interface RoleDao extends BaseDao<Role>{
    
    }

    PS:此处省略创建 User 与 Role 类过程

    3)定义一个 通用的 Dao 接口实现类(如:BaseDaoImpl 实现类),实现 通用的增删改查方法

    BaseDaoImpl 类代码:

    package cn.oa.base;
    
    import java.lang.reflect.ParameterizedType;
    import java.util.Collections;
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    
    @SuppressWarnings("unchecked")
    public class BaseDaoImpl<T> implements BaseDao<T> {
    
    	@Resource
    	private SessionFactory sessionFactory;
    	protected Class<T> clazz;
    
    	public BaseDaoImpl() {
    		// 通过反射得到T的真实类型
    		ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
    		this.clazz = (Class) pt.getActualTypeArguments()[0];
    		System.out.println("clazz = " + clazz.getName());
    	}
    
    	protected Session getSession() {
    		return sessionFactory.getCurrentSession();
    	}
    
    	@Override
    	public void save(T t) {
    		getSession().save(t);
    	}
    
    	@Override
    	public void delete(Long id) {
    		Object obj = getSession().get(clazz, id);
    		getSession().delete(obj);
    	}
    
    	@Override
    	public void update(T t) {
    		getSession().update(t);
    	}
    
    	@Override
    	public T getById(Long id) {
    		return (T) getSession().get(clazz, id);
    	}
    
    	public List<T> getByIds(Long[] ids) {
    		if (ids == null || ids.length == 0) {
    			return Collections.EMPTY_LIST;
    		}
    
    		return getSession().createQuery(//
    				"FROM " + clazz.getSimpleName() + " WHERE id IN(:ids)")//
    				.setParameterList("ids", ids)//
    				.list();
    	}
    
    	public List<T> findAll() {
    		return getSession().createQuery(//
    				"FROM " + clazz.getSimpleName())//
    				.list();
    	}
    
    }
    

    PS:①、在 BaseDaoImpl 类中定义 getSession() ,任何继承 BaseDaoImpl 的实现类中都可以直接通过 getSession() 获得 Session

            ②、在 父类构造方法中,通过反射获取 泛型的类型

    4)所有的 Dao 接口实现类 继承 BaseDaoImpl 实现类

    UserDaoImpl 类代码:

    package cn.oa.dao.impl;
    
    import cn.oa.base.BaseDaoImpl;
    import cn.oa.dao.UserDao;
    import cn.oa.model.User;
    
    public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {
    
    
    }
    

    RoleDaoImpl 类代码:

    package cn.oa.dao.impl;
    
    
    import cn.oa.base.BaseDaoImpl;
    import cn.oa.dao.RoleDao;
    import cn.oa.model.Role;
    
    public class RoleDaoImpl extends BaseDaoImpl<Role> implements RoleDao {
    
    
    }
    

     

    展开全文
  • 团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是了个实现类。于是遇到错误了。 找不到那个方法。问...


    团队开发一个项目,由老大架了一个框架,遇到了DAO层不用写接口了,我也是用了2次才记住这个事的,因为自己一直都是习惯于写DAO层的实现类,所以,习惯性的还是写了个实现类。于是遇到错误了。

    找不到那个方法。问了团队的人才知道,方法名和Mapper中配置的id名必须一样。

    实现:

    一、配置Spring集成MyBatis:

    <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"...
    
    -------------------------------------------------
    
    <!-- 配置数据源 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" ... ------------------------------------------------- <!-- 产生sqlsessionfactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml" /> </bean> --------------------------------------------------

    要实现对数据库的操作必须要有sqlSession,而sqlSession是由sqlSessionFactory创建的。我们可以在spring配置好bean。

    <!-- 自动扫描mapper接口-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.xiaojuzi.chaojijuhui.**.dao" p:sqlSessionFactoryBeanName="sqlSessionFactory" />

    这个配置就是配置映射文件的路径,这样做的好处就是不用再写Dao的实现类了,也就是说,我们写好接口,写好配置文件,会自动映射到对应的方法和sql语句。

    二、开发mapper.xml映射文件

    <mapper namespace="com.xiaojuzi.chaojijuhui.user.dao.UserDao">
    
    -------------------------------------------------------

    在这里只有一个UserDao(被代理的接口)。 
    user.mapper.xml–namespace配置的就是UserDao的包全名。

    三、开发mapper.java的接口

         /**
         * 根据用户的用户名查询用户
         * @param user
         * @return
         */
        User queryUserByLoginName (String loginName);
    
        /**
         * 用户通过手机号码去修改密码
         * @param userModel * @return */ Boolean updatePasswordByMobile(UserModel userModel); 

    如果需要特定类型的参数,就自己再造一个POJO类(例如:UserModel)。

     <sql id="userColumns">
            u.id,
            u.login_name as "loginName",
            u.head_img as "headImg", ... --------------------------------------------- <select id="queryUserByLoginName" resultType="User" parameterType="User"> SELECT <include refid="userColumns" /> FROM juhui_user u WHERE u.login_name = #{loginName} and u.del_flag = #{DEL_FLAG_NORMAL} </select> <update id="updatePasswordByMobile" parameterType="UserModel"> update juhui_user set update_date=DATE_FORMAT(#{updateDate}, '%Y-%m-%d %H:%i:%S'), salt = #{salt}, password = #{password} where mobile = #{mobile} </update>

    这里mapper.xml的(select、insert、update..)标签的id必须和DAO接口的方法名一样!

    Mapper开发规则

    1、 在mapper.xml中将namespace设置为mapper.Java的全限定名 
    2、 将mapper.java接口的方法名和mapper.xml中statement的id保持一致。 
    3、 将mapper.java接口的方法输入参数类型和mapper.xml中statement的parameterType保持一致 
    4、 将mapper.java接口的方法输出 结果类型和mapper.xml中statement的resultType保持一致

    注意遵循上边四点规范!

    这样抛弃Dao实现类的写法: 
    具有更好的可扩展性,提高了灵活度。

    原理

    再根据网上的一些知识点,讲一下原理:

    mybatis通过JDK的动态代理方式,在启动加载配置文件时,根据配置mapper的xml去生成Dao的实现。

    session.getMapper()使用了代理,当调用一次此方法,都会产生一个代理class的instance,看看这个代理class的实现.

    public class MapperProxy implements InvocationHandler { ... public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) { ClassLoader classLoader = mapperInterface.getClassLoader(); Class<?>[] interfaces = new Class[]{mapperInterface}; MapperProxy proxy = new MapperProxy(sqlSession); return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (!OBJECT_METHODS.contains(method.getName())) { final Class<?> declaringInterface = findDeclaringInterface(proxy, method); final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession); final Object result = mapperMethod.execute(args); if (result == null && method.getReturnType().isPrimitive()) { throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; } return null; } 

    这里是用到了JDK的代理Proxy。 newMapperProxy()可以取得实现interfaces 的class的代理类的实例。

    当执行interfaces中的方法的时候,会自动执行invoke()方法,其中public Object invoke(Object proxy, Method method, Object[] args)中 method参数就代表你要执行的方法.

    MapperMethod类会使用method方法的methodName 和declaringInterface去取 sqlMapxml 取得对应的sql,也就是拿declaringInterface的类全名加上 sql-id..

    总结: 
    这个就是利用JDK的代理类实现的。


    展开全文
  • 我的dao层写的是接口,,,,实现是mapper.xml 现在我要拦截 dao 层的 update 和delete方法, 但不管我 用jdk的动态代理 还是 cglib 的代理 都无法拦截, 但有趣的是service 层可以拦截,,,,但也只能拦截 用业务对象访问的...
  • 1、使用Spring开发中,service接口或者是dao层接口无法注入,遇dao到这种情况,首先检查dao层和service层是否被扫描到。 原因:service层为扫描到 &lt;!-- 注解扫描包,注意换成自己的路径 --&gt; &...
  • idea显示行号: 显示打断点的地方: dao层接口的方法不能用update,delete,会与自带的方法命冲突:
  • MyBatis xml和dao层接口组合使用

    千次阅读 2017-06-01 11:38:01
    mybatis可以用xml进行数据操作,也可以在dao层用注解的方式,也可以采取xml和dao层接口组合使用的方法。显然 ,后者更加简单。 实体类Student package com.zhao.entity; /** * * @author: zhao * @time...
  • Spring中DAO层接口的单元测试

    千次阅读 2016-05-08 21:30:57
    基本上所有的java应用都会跟数据库打交道,DAO层接口的测试涉及到数据库测试数据的准备、维护、验证及清理。单元测试应该要具有可重复性、独立性,所以单元测试的数据不应该污染数据库。很多人在DAO层接口的单元测试...
  • 这段时间在看JavaWeb的视频时候,产生了一个疑惑,为什么Dao层和Service层要写接口和实现类? 这里是引用 接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,可以将其理解成一种规范.如果项目中,Dao层或Service层...
  • 参考mybatis入门基础(二)----原始dao的开发和mapper代理开发 ...其实也就是通过接口名与mapper的id绑定在一起,通过SQL去实现类,返回数据。 转载于:https://www.cnblogs.com/yncx/p/7680990.html...
  • 举个例子,用DAO接口,那么持久用Hibernate,还是用iBatis,还是 JDBC,随时可以替换,不用修改业务Service类的代码。 不用接口的话,假如修改了dao中的代码,因为service引用了dao中的类,那么也要改变service...
  • 1、使用Spring开发中,service接口或者是dao层接口无法注入,遇dao到这种情况,首先检查dao层和service层是否呗扫描到。 2、1不能解决则查看service接口是否有注解标示,或者检查自己的dao和service对应配置文件...
  • 基于springdata JPA的dao层接口实现

    千次阅读 2019-07-07 16:35:48
    基于springdata JPA的dao层接口实现以及分页 1、【举例】 只需要继承 JpaRepository<实体类,主键类型> package com.tmall.tmallspringboot.dao; import com.tmall.tmallspringboot.pojo.Category; import ...
  • MyBatis xml和dao层接口组合使用,一共有三种方法 具体见此文MyBatis xml和dao层接口组合使用   本文主要是在NewsDAO 中的查询语句出错 出错时,采用的方法是 xml+dao dao代码的位置位于:src/java/...
  • mapper.xml是怎样实现Dao层接口

    万次阅读 2017-09-04 21:21:49
    下午就开始想mybatis是怎么通过xml文件来实现dao层接口的,一开始想直接用Class.forName(String name)然后调用getMethods的办法来得到Dao接口所有的方法Method,结果证明这对接口实没有用的,会报一个错误。...
  • ssm框架dao层接口无法自动注入问题

    千次阅读 2019-08-01 11:02:53
    这个问题实在奇葩,我也没看过框架源码,估计就算看了也看不懂,目前发现在dao层接口上不能使用@Component注解,但是可以使用@Repository,也可以啥注解都不加。根据目前的情况观察只要加上了@Component注解在容器里...
  • 使用@Repository来注解,来注解dao层接口,运行运行项目不能扫描 应该是接触的项目比较少,第一次遇到这种情况,使用@Repository注解mapper接口发现项目运行找不到dao层的东西,我滴个神!!!以前用着这玩意不是挺好...
  • DAO层接口,为什么能操作数据库

    万次阅读 多人点赞 2018-11-09 20:14:22
    public interface TestDAO { Test selectById(Integer id); } 一、问题:  如上代码所示,为什么调用TestMapper的selectById... 接口当然是不能直接调用方法的,那么接口的实现类呢?应该是mybatis框架,自动...
  • DAO层接口定义多个入参(Mybatis)

    千次阅读 2017-06-26 20:59:38
    大部分人也是从自动生成的sql接口来了解DAO层接口的定义。 观察generator自动生成的代码可以发现在多个入参(多个PrimaryKey)的情况下,会将它们定义为一个实体类,用实体类作为入参。 以类P
  • SSH中公共Dao层、Dao接口

    千次阅读 2017-11-23 20:45:54
    为了优化代码可以创建公共类与接口一个Dao层的公共接口,在里面实现增删改查等各种功能 public interface IBaseDao { //增加 public void save(T entity); //删除 public void delete(Long id); //...
  • 实体类 /** * 1.实体类和表的映射关系 * @Eitity * @Table * 2.类中属性和表中字段的映射关系 * @Id * @GeneratedValue * @Column */ @Data @Entity @Table(name="cst_customer") public class Customer... ...
  • springboot启动扫描不到dao层接口

    千次阅读 2019-01-16 16:24:52
    Field girlDao in com.example.springbootdemo.service.impl.GirlServiceImpl required a bean of type 'com.example.springbootdemo.dao.GirlDao' that could not be found.   ...),dao包的...
  • 其实也就是通过接口名与mapper的id绑定在一起(即相同),通过SQL去实现类,返回数据。 转载于:https://www.cnblogs.com/liuqing576598117/p/10600161.html
  • 模拟原生Dao层开发 (1) DAO接口类UserMapper.java  package com.lic.ibatis.dao; import com.lic.ibatis.entity.User; public interface UserMapper { //根据用户ID获取用户信息 User getUserById(int id)...
  • 我自己写dao 层接口都是自己加上@Repository这个注解,但是项目组的其他同事不的情况也可以正常注入?带着这个疑问我一点一点查找资料。最终发现 MapperScannerConfigurer 帮我们做了实例化bean的工作。在Spring...
  • 接着之前的springboot项目,来springboot对dao层接口测试。 正文 之前我的springboot项目在初始化就自动引入了测试依赖,所以无需再次引入其他。 新增函数 mybatis自动生成了6个基本函数,但是开发中这些函数远远...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 132,094
精华内容 52,837
关键字:

dao层接口怎么写