权记

一个关于我们生活点滴的网站,一个记录我们酸甜苦辣的日志。

 

Drawing in code

之前介绍了一个Swing做的原子钟的效果 “Swing Nixieclock(原子钟)效果”,作者放出了制作 Swing原子钟 制作的主要元素 – 原子钟的制造过程。

想看制作过程的移步这里:

Part One

先通过绘图工具,绘出原型,绘制过程以及绘制时的参数,要在Swing中使用。

Part Two

将原型绘制过程中的一些数据,以Java Code 的形式在Swing中体现出来,步骤和通过绘图工具绘制时差不多,一步步来。

  • 0 Comments
  • Filed under: 技术
  • 在JavaEye上看的这个,转发一下,原文:http://www.javaeye.com/news/13151

    Gerrit 受到了这些设计的启发,尝试着使用Swing创造出了NixieClock(原子钟)效果。

    *首先,在Fireworks中创建了一个原型

    *第二步是建立一个JavaBean并将其转成Swing,这个步骤要花费很多时间

    *第三步是建立一个包含时钟逻辑的JFrame和6个nixie number的组件

    结果如下:

    下载NixieClock:http://www.jug-muenster.de/wp-content/uploads/2010/01/NixieClock.zip

  • 1 Comment
  • Filed under: 技术
  • 本周主题:Java Net and XML Parser

    分两部分:一部Java Net(大部分来自Java Doc), 一部分XML Parser, 通过一个Twitter的例子来描述简单的使用情况。

    一、Java Net

    java.net 包的概述:

    java.net包大致可分为两部分:

    低级API – 用于处理以下抽象:

    • 地址,也就是网络标识符,如 IP 地址。
    • 套接字,也就是基本双向数据通信机制。
    • 接口,用于描述网络接口。

    高级API – 用于处理以下抽象:

    • URI,表示统一资源标识符。
    • URL,表示统一资源定位符。
    • 连接,表示到 URL 所指向资源的连接。

    地址

    在整个 java.net API 中,地址或者用作主机标识符或者用作套接字端点标识符。

    InetAddress 类是表示 IP(Internet 协议)地址的抽象,它拥有两个子类:

    但是,在大多数情况下,不必直接处理子类,因为 InetAddress 抽象应该覆盖大多数必需的功能。

    套接字

    套接字是在网络上建立机器之间的通信链接的方法。java.net 包提供 4 种套接字:

    • Socket 是 TCP 客户端 API,通常用于将 (java.net.Socket.connect(SocketAddress)) 连接到远程主机。
    • ServerSocket 是 TCP 服务器 API,通常接受 (java.net.ServerSocket.accept) 源于客户端套接字的连接。
    • DatagramSocket 是 UDP 端点 API,用于发送和接收 java.net.DatagramPackets
    • MulticastSocket 是 DatagramSocket 的子类,在处理多播组时使用。

    使用 TCP 套接字的发送和接收操作需要借助 InputStream 和 OutputStream 来完成,这两者是通过 java.net.Socket.getInputStream 和 java.net.Socket.getOutputStream 方法获取的。

    高级API

    java.net 包中的许多类可以提供更加高级的抽象,允许方便地访问网络上的资源。这些类为:

    • URI 是表示在 RFC 2396 中指定的统一资料标识符的类。顾名思义,它只是一个标识符,不直接提供访问资源的方法。
    • URL 是表示统一资源定位符的类,它既是 URI 的旧式概念又是访问资源的方法。
    • URLConnection 是根据 URL 创建的,是用于访问 URL 所指向资源的通信链接。此抽象类将大多数工作委托给底层协议处理程序,如 http 或 ftp。
    • HttpURLConnection 是 URLConnection 的子类,提供一些特定于 HTTP 协议的附加功能。

    建议的用法是使用 URI 指定资源,然后在访问资源时将其转换为 URL。从该 URL 可以获取 URLConnection 以进行良好控制,也可以直接获取 InputStream。

    下面是一个示例:

    URI uri = new URI("http://java.sun.com/");
    URL url = uri.toURL();
    InputStream in = url.openStream();
    

    URLConnection 和 HttpURLConnection 的使用:

    抽象类 URLConnection 是所有表示应用程序与 URL 之间通信链路的类的超类。该类的实例可以用来对由 URL 引用的资源进行读取和写入操作

    HttpURLConnection 仅是支持了 HTTP 特定功能的 URLConnection

    1. 通过调用 URL 的 openConnection 方法产生一连接对象。
    2. 操纵设置参数和一般请求属性。
    3. 使用 connect 方法,实现对远程对象的实际连接。
    4. 远程对象一旦可用,就可以访问远程对象的域和内容。

    通过如下的方法修改设置参数:

    • setAllowUserInteraction
    • setDoInput
    • setDoOutput
    • setIfModifiedSince
    • setUseCaches

    通过使用如下的方法修改一般请求属性:

    • setRequestProperty

    AllowUserInteractionUseCaches 参数的缺省值可以通过使用 setDefaultAllowUserInteractionsetDefaultUseCaches 方法来设置。 而一般请求属性的缺省值可以通过方法 setDefaultRequestProperty 来设置 。

    上面的每个 set 方法都有相应 get 方法来获取参数和一般请求属性的值。可用的特定参数和一般请求属性是特定于协议的。

    对远程对象的连接完成后,用下列方法访问报头域和内容:

    • getContent
    • getHeaderField
    • getInputStream
    • getOutputStream

    确认报头域能够被频繁访问。方法:

    • getContentEncoding
    • getContentLength
    • getContentType
    • getDate
    • getExpiration
    • getLastModified

    提供对这些域的便利访问。 getContentType 方法由 getContent 方法调用以确定远程对象的类型; 子类来覆盖方法 getContentType 可能是有利的。

    下面是一个通过Twitter API去获信息的demo:

            URL url = new URL("http://t.bbercn.com/statuses/user_timeline.xml");
            String username = (String) params[0];
            String password = (String) params[1];
            String user_paw = username + ":" + password;
            String encoding = "Basic " + Base64Converter.encode(user_paw.getBytes());
            URLConnection uc = url.openConnection();
            uc.setRequestProperty("Authorization", encoding);
            InputStream content = (InputStream) uc.getInputStream();
    

    剩下的工作就是对InputStream 进行本地处理,Twitter返回的是XML,那么我们下面就对XML进行解析,获取我们要获取的信息。

    XML Parser

    关于 xml 解析的工具有很多,具体可以看这里:http://www.open-open.com/31.htm 罗列的一些。

    下面通过Commons-Digester来处理上面我们获取的InputStream

    public class ParseTools {
    
        private List statuses = new ArrayList<Status>();
    
        public List<Status> getStatuses() {
            Collections.sort(statuses);
            return statuses;
        }
    
        public void parseStatus(InputStream stream) throws IOException, SAXException {
            Digester digester = new Digester();
            digester.setValidating(false);
            digester.push(this);
    
            digester.addObjectCreate("statuses/status", Status.class);
            //status
            digester.addBeanPropertySetter("statuses/status/id");
            digester.addBeanPropertySetter("statuses/status/created_at");
            digester.addBeanPropertySetter("statuses/status/text");
            digester.addBeanPropertySetter("statuses/status/source");
            digester.addBeanPropertySetter("statuses/status/truncated");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_status_id");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_user_id");
            digester.addBeanPropertySetter("statuses/status/favorited");
            digester.addBeanPropertySetter("statuses/status/in_reply_to_screen_name");
            //user
            digester.addObjectCreate("statuses/status/user", User.class);
            digester.addBeanPropertySetter("statuses/status/user/id");
            digester.addBeanPropertySetter("statuses/status/user/name");
            digester.addBeanPropertySetter("statuses/status/user/screen_name");
            digester.addBeanPropertySetter("statuses/status/user/description");
            digester.addBeanPropertySetter("statuses/status/user/location");
            digester.addBeanPropertySetter("statuses/status/user/profile_image_url");
            digester.addBeanPropertySetter("statuses/status/user/url");
            digester.addBeanPropertySetter("statuses/status/user/isProtected");
            digester.addBeanPropertySetter("statuses/status/user/followers_count");
            digester.addBeanPropertySetter("statuses/status/user/friends_count");
            digester.addSetNext("statuses/status/user", "setUser");
            digester.addSetNext("statuses/status", "addStatuses", Status.class.getName());
            digester.parse(stream);
        }
    
        public void addStatuses(Status status) {
            Status d = new Status();
            d.setId(status.getId());
            d.setCreated_at(status.getCreated_at());
            d.setFavorited(status.isFavorited());
            d.setText(status.getText());
            d.setSource(status.getSource());
            d.setTruncated(status.isTruncated());
            d.setIn_reply_to_screen_name(status.getIn_reply_to_screen_name());
            d.setIn_reply_to_status_id(status.getIn_reply_to_status_id());
            d.setIn_reply_to_user_id(status.getIn_reply_to_user_id());
            d.setUser(status.getUser());
            getStatuses().add(d);
        }
    }
    
  • 0 Comments
  • Filed under: 周主题
  • 周主题推迟一周

    于最近另一个项目的启动,工作方面基本上是掐着时间在忙,所以周主题的任务就只能稍微往后推一周了。

    预计本周四结合公司的培训内容,出一个有关JQuery的入门介绍,这个内容顺便就补给上周的周主题。

    顺便预告下:接下来这周和下周会整理有关Java.Net包下一些常用类的使用,包括HttpClient的使用,以及XML Parser方面的汇总。

  • 0 Comments
  • Filed under: 周主题
  • 择这个主题,是因为上周公司刚好做了一个有关“Beginning Java AWT and Swing” 的培训,借此机会正好总结一下这方面的使用技巧。对于Swing界面方面的研究,我仅仅是应用而已,公司里有几位同事在这方面的研究比较深,这块的应用和使用技巧分两部分, 一部分是Swing 使用本身的,另一部分是Design工具NetBeans的,如果是刚入门建议从这里看起:Creating a GUI With JFC/Swing

    本期主题:Swing Tips

    一、性能问题

    随着Java 6对于Swing性能的改进,Swing的运行速度已经开始得到了大大的提高,看看NetBeans就知道了,NetBeans就是Swing开发的,如果你跑Swing很慢,很耗资源,那么应该从自己的程序上找找问题,推荐使用NetBeans自带的Profile查找原因,教程在这里 – Profile Introduce

    二、LookAndFeel

    用Swing做企业应用时,LookAndFeel的选择和使用是决定这个项目能否被客户接受的一个很大因素,即要好看,又要考虑跨平台的兼容性,必要时自己还得设计部分LookAndFeel, 可以看看这里提供的一些开源LookAndFeel:http://www.open-open.com/61.htm 和http://www.javootoo.com/。
    切换LookAndFeel:

    UIManager.setLookAndFeel(LookAndFeelName);
    SwingUtilities.updateComponentTreeUI(frame);
    frame.pack();
    

    三、合理的控制初始化组件和组件初始化的顺序可以很大的提高性能

    举个例子:之前我们项目中有一个地方,当打开程序时,会初始化几十个甚至成百个JPanel,这显然成为程序启动时慢的一个因素,也导致了用户体验的降低,这些JPanel完全可以在程序启动后再根据用户的需要去初始化,因为用户打开程序时这些Panel不是必须看到的。

    四、要有统一的UI规范

    比如Button的高度,进度条的高宽等,也可以通过UIManager给系统组件设置统一属性,比如统一设定Button的间距和字体:

    UIManager.put("Button.margin", new Insets(2, 5, 2, 5));
    UIManager.put("Button.font", new Font("宋体", Font.PLAIN, 13));
    

    五、多线程的使用

    用Swing做的都是界面的东西,如果界面假死或者用户等待事件太长,那么用户体验必然是不好的,这里就需要用到多线程的使用了,当界面处理一个请求时,不能让界面假死了,需要后台另一个线程去做处理,然后将结果返回到Swing线程,这块可以看看SwingWorker的介绍。

    六、布局管理器

    布局管理器的使用在Swing里面是比较重要的,它直接决定了你界面的显示效果,也是比较难用的一块,不好举例子,建议多了解每个布局管理器的使用场景。

    七、JTable & JTree

    在Swing组件的使用中除了布局管理器,估计就数JTable和JTree的使用稍微有点麻烦了,下面我就分享一些实际项目中JTable的一些实例,关于JTree,可以点这里:JTree 经验 总结

    JTable相关

    1、自定义表头排序
    TableRowSorter rs = (TableRowSorter) table.getRowSorter();
    Comparator<Integer> intComparator = new Comparator<Integer>() {
    
                public int compare(Integer o1, Integer o2) {
                    return o1.compareTo(o2);
                }
            };
    rs.setComparator(3, intComparator);
     
    2、自定义Table Renderer
    public class CommonTableCellRenderer extends DefaultTableCellRenderer {
    
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value,
                    boolean isSelected, boolean cellHasFocus, int row, int column) {
                JComponent comp = (JComponent) super.getTableCellRendererComponent(table, value,
    isSelected, cellHasFocus, row, column);
                //表格的奇数偶数行交叉颜色显示
                if (!isSelected) {
                    if (row % 2 == 0) {
                        comp.setBackground(UIConsts.HIGHLIGHTER_COLOR);
                    } else {
                        comp.setBackground(Color.white);
                    }
                }
    
                //table column里面显示图标和对齐方式
                switch (column) {
                    case PaperTableModel.STATUS_COLUMN:
                        switch ((EntityStatus) value) {
                            case VALID:
                                comp.setIcon(ENABLED_ICON);
                                setHorizontalAlignment(JLabel.LEADING);
                                break;
                            case DISABLED:
                                comp.setIcon(DISABLED_ICON);
                                setHorizontalAlignment(JLabel.CENTER);
                                break;
                        }
                        break;
                    default:
                        comp.setIcon(null);
                        break;
                }
    
                return comp;
            }
        }
     
    3、自定义Table列宽
    TableColumnModel colModel = table.getColumnModel();
    colModel.getColumn(0).setPreferredWidth(70);
    colModel.getColumn(1).setPreferredWidth(55);
    colModel.getColumn(2).setPreferredWidth(120);
    
    4、禁止Table列拖动
     table.getTableHeader().setReorderingAllowed(false);  
    5、单选表格设置
     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);   
    6、设置表头默认支持排序
     table.setAutoCreateRowSorter(true);  
    7、设置列不可随容器组件大小变化自动调整宽度
     table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);  
    8、固定Table上的某些列不滚动

    这个需求是这样的,比如Table上左边有部分数据,是后面数据所共有的属性,那么当后面数据很多时,显示不下会出现滚动条,但是滚动时又不想让左侧的共有属性动,只滚动右侧的数据部分。
    实现原理是:scrollPane里面放置一个表格,然后在scrollPane的左上角放置以共有属性的部分为Model的表格,剩下的右侧就是剩余的纯数据表格。
    最终效果就是表格左侧的列锁定了,右侧数据出现滚动条时,可以滚动,但左侧不动。
    核心代码:比如有HeaderTable和ReportTable, 其中ReportTable是放置在一个ScrollPanel里面,Model是所有数据的Model,将左侧的数据和右侧的数据分开

    //找到主表所在的scrollPane
    JScrollPane scrollPane = (JScrollPane) SwingUtilities.
       getAncestorOfClass(JScrollPane.class,
        reportTable.getTable());
    
    //中间处理headerTable的数据和reportTable剩余的数据
    
    //将新表HeaderTable放在scrollPane的左上角
    scrollPane.setRowHeaderView(headerTable.getTable());
    scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
                        headerTable.getTable().getTableHeader());
    
    9、Table上的直接编辑功能

    两点:
    1、重写 public boolean isCellEditable(int row, int columnIndex) 方法,定义可编辑的行列。
    2、重写 public void setValueAt(Object obj, int rowIndex, int columnIndex) 方法,拿到原来的对象,设置新的对象值。

    10、Table的Excel导出功能

    表格上的Excel导出功能还是比较实用的功能,企业应用一般都会用到,这里提供相关代码

    try {
       WritableCellFormat titleFormat = new WritableCellFormat(
       new WritableFont(WritableFont.createFont("黑体"), 16,
       WritableFont.NO_BOLD));
       titleFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 垂直对齐
       titleFormat.setAlignment(Alignment.CENTRE); // 水平对齐
       titleFormat.setWrap(true); // 是否换行
    
       WritableCellFormat headerFormat = new WritableCellFormat();
       headerFormat.setBorder(Border.ALL, BorderLineStyle.THIN); // 线条
       headerFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 垂直对齐
       headerFormat.setAlignment(Alignment.CENTRE); // 水平对齐
       headerFormat.setWrap(true); // 是否换行
    
       WritableCellFormat countFormat = new WritableCellFormat(
       new NumberFormat("0.000"));
       countFormat.setBorder(Border.ALL, BorderLineStyle.THIN); // 线条
       countFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 垂直对齐
       countFormat.setAlignment(Alignment.RIGHT); // 水平对齐
       countFormat.setWrap(true); // 是否换行
    
       WritableCellFormat moneyFormat = new WritableCellFormat(
       new NumberFormat("0.00"));
       moneyFormat.setBorder(Border.ALL, BorderLineStyle.THIN); // 线条
       moneyFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 垂直对齐
       moneyFormat.setAlignment(Alignment.RIGHT); // 水平对齐
       moneyFormat.setWrap(true); // 是否换行
    
       WritableCellFormat intFormat = new WritableCellFormat(
       new NumberFormat("0"));
       intFormat.setBorder(Border.ALL, BorderLineStyle.THIN); // 线条
       intFormat.setVerticalAlignment(VerticalAlignment.CENTRE); // 垂直对齐
       intFormat.setAlignment(Alignment.RIGHT); // 水平对齐
       intFormat.setWrap(true); // 是否换行
    
       WritableWorkbook book = Workbook.createWorkbook(exportFile);
       WritableSheet sheet = book.createSheet("综合统计报表", 0);
       int titleRow = 0;
       int headerRow = 3;
       int dataRow = 5;
       sheet.mergeCells(0, titleRow, model.getColumnCount() - 1,
       titleRow);
       Label titleLab = new Label(0, titleRow, tableTitleTxfd.getText(), titleFormat);
       sheet.addCell(titleLab);
    
       //生成表头
       for (int j = 0; j < model.getColumnCount(); j++) {
           sheet.mergeCells(j, headerRow, j, headerRow + 1);
           Label lab = new Label(j, headerRow, model.getColumnName(
          j), headerFormat);
           sheet.addCell(lab);
       }
    
       for (int row = 0; row < model.getRowCount(); row++) {
           for (int col = 0; col < model.getColumnCount(); col++) {
              Object obj = model.getValueAt(row, col);
              if (obj instanceof String) {
                 Label lab = new Label(col, dataRow + row,
                   (String) obj, headerFormat);
                sheet.addCell(lab);
              } else if (obj instanceof Integer) {
             Number labelN = new Number(col, dataRow
             + row, (Integer) obj, intFormat);
             sheet.addCell(labelN);
             } else {
             Label lab =
             new Label(col, dataRow + row, "",
             headerFormat);
             sheet.addCell(lab);
             }
          }
       }
    
       //生成表尾
       int footerRow = dataRow + model.getRowCount() + 1;
       int step =
       (int) (((double) (model.getColumnCount() - 2) / 3)
       + 1);
       Label footerLab = new Label(0, footerRow, "部门负责人:");
       sheet.addCell(footerLab);
       footerLab = new Label(step, footerRow, "制表:" + creatorTxfd.getText());
       sheet.addCell(footerLab);
       footerLab = new Label(model.getColumnCount() - 2, footerRow,
       NazcaFormater.getSimpleDateString(new Date()));
       sheet.addCell(footerLab);
    
       sheet.setColumnView(0, 16);
       sheet.setRowView(titleRow, 600);
    
       book.write();
       book.close();
       //导出成功
       } catch (Throwable ex) {
       //导出报表失败
       ex.printStackTrace();
    }
    
    11、Table的打印功能

    打印这块,之前已经提过,可以参考之前的文章 JTable Print

    12、在Table上选择多行
    int rowcounts=table.getSelectedRows().length;
    if(rowcounts>1)
      int[] rows=table.getSelectedRows();
        for(int i=0;i<rows.length;i++){
        String value=(String) tableModel.getValueAt(i, 1);
      }
    }
    

    NetBeans 6+ Tips

    这里说NetBeans,主要是因为目前Swing开发方面,还没有哪个IDE能胜过NetBeans。

    一、NetBeans的配置

    1、配置为英文,大部分时间我们下载的都是中文的版本,可以通过在/$NetBeans_HOME/etc/netbeans.conf中添加 –locale en_US, 让启动时显示为英文,这个之前也有文章介绍:Netbeans 英文界面最简单的Netbeans中英文切换
    2、优化配置可以看之前介绍的这篇文章,就不重复了:Netbeans 6.5 优化建议

    二、经验分享

    1、Swing的Debug虽然被很多人说不好用,但是在用NetBeans时,多用Debug可以提高效率,因为Debug模式下的修改,大部分只要点击应用,就可以不用重启项目而看到效果。
    2、很好用的快捷键和快速补齐(限Windows + Linux下,如果在Mac下改成 ⌘ 试试),如:

    快捷键:
    Ctrl+R          Rename
    Alt+Enter       Fix Error(Eclipse Ctrl+1)
    Alt+Shift+F    Quick Format
    Alt+Shift+I     Fix Import
    F9                Build File
    F6                Run Main Project
    Shift+F6        Run File
    Ctrl+|           Insert Code
    
    快速补齐(英文输入状态下,输入完后按Tab键,也可以自己配置为其他键,在Options - Editor -
    Code Templates下):
    psvm            public static void main
    sout             System.out.println
    im               implements
    Psfs             public static final String
    psfi             private static final String
    fore            for($ : $){}
    fori             for(int i = 0; i < arr.length; i++){}
    forl             for(int i = 0; i < list.size(); i++){}

    三、插件分享
    我们都知道NetBeans上的插件很多,可以说NetBeans正是因为这些插件才强大起来,支持的功能也更多了。分享的这几个插件是平时工作时,可以显著提高效率的,不好的不推荐,你如果有好的也别忘记分享下。

    1、Path Tools – 可以直接查找到类或者文件夹所在的磁盘位置,基本是我每次装完NetBeans的后第一个装的插件。
    2、SQE(Software Quality Environment) – 是最近同事刚刚推荐的一个插件,看名字就知道了,是一个类似Firebug的插件,可以发现程序中存在的一些显著的错误,很不错。
    3、UUID Generator - 同事写的一个快速生成UUID的插件。
    4、SwingX 插件 – 用来添加一些SwingX组件的。
    5、iReport – 打印报表用的插件,结合JasperReport使用。

  • 0 Comments
  • Filed under: 周主题
  • [09/48周主题] – JPA

    是本博客开始周主题的第一个主题,周主题计划按自然周为标记,记录一年内每周的关注主题,这样至少可以提醒自己哪个周的没有写了。

    本期主题:JPA

    对于JPA不熟悉的朋友可以先看看这里:百度百科之JPA 里面介绍了JPA的起源,优势,厂商等。

    本文是由网上其他网友的经验和自己的经验总结而成,个人能力有限,不免有错误之处,如有误导之地恳请指正。欢迎大家提出宝贵意见,以便完善。
    context

    一、JPA中必须知道的4个状态

    1. 新建态(New):新创建的实例对象,没有持久化主键。

    简单讲就是你刚new出来的对象,谁也管不着的。

    2. 受控态(Managed):在持久化上下文中受管理的对象;

    先理解两个概念持久化主键持久化上下文:持久化上下文是由与该上下文相关联的 EntityManager 实例管理的一组实体实例,关键词是EntityManager, 新创建的实体尚未被托管于持久化上下文中,EntityManager无法对它进行管理。EntityManager对持久化上下文中的实体进行管理,必然通过主键去查找实体对象,这个主键就是持久化主键。

    3. 游离态(Detached):游离于持久化上下文之外的实例对象;

    4. 删除态(Removed):被删除的实例对象。

    这些状态通过调用EntityManager的接口方法进行相互迁移

    二、EntityManager 的常用API

    void persist(Object entity)

    • 通过persist()方法,新建状态将转换为受控状态。这意谓着当persist ()方法所在的事务提交时,实体的数据将保存到数据库中。
    • 如果实体已经被持久化,那么再调用persist()操作,会抛出异常 Duplicate entry。
    • 如果对一个已经删除的实体调用persist()操作,删除态的实体又转变为受控态,实体的数据将保存到数据库中。
    • 如果persist的是一个游离实体(即上下文中没有它),而上下文中又没有它的受管版本,数据库却有这个实体,那么会抛出异常 Duplicate entry;
    • 如果persist的是一个游离实体(即上下文中没有它),而上下文中却有它的受管版本,数据库中也有了这个实体,那么EntityManager在persist它的时候就会抛出异常:javax.persistence.EntityExistsException
    • 在一个实体上调用persist()操作,将广播到和实体关联的其他实体上,执行相应的级联持久化操作;

    void remove(Object entity)

    • 通过remove()方法删除一个受控的实体;
    • 如果实体声明为级联删除(cascade=REMOVE 或者cascade=ALL ),被关联的实体也会被删除;
    • 在一个新建状态的实体上调用remove()操作,将被忽略;
    • 如果在游离实体上调用remove()操作,将抛出 IllegalArgumentException,相关的事务将回滚;
    • 如果在已经删除的实体上执行remove()操作,也会被忽略

    T merge(T entity)

    • 将一个游离态的实体持久化到数据库中,并转换为受控态的实体;
    • Merge一个游离版本,同时上下文中却有它的受管版本,那么返回的是受管版本,数据库中的数据更新

    Query createQuery(String qlString)

    • 根据JPA的查询语句创建一个查询对象Query

    T find(Class entityClass, Object primaryKey)

    • 以主键查询实体对象,entityClass是实体的类,primaryKey是主键值

    三、使用时应注意的一些情况

    1. EntityManager对象的事务管理方式有两种,分别为JTA和RESOURCE_LOCAL,即Java Transaction API方法和本地的事务管理。JPA中的事务类型通过persistence.xml文件中的“transaction-type”元素配置。JTA事务只能运行在J2EE的环境中,即EJB容器中和Web容器中;而在J2SE环境中只能使用RESOURCE_LOCAL管理事务。

    2. 注意OneToMany,ManyToOne,ManyToMany三种关系时的参数配置.

    3. Merge也有Persist的功能,数据库中有就merge,没有添加。

    4. 注意Transaction的使用,不要一个方法中有几个Transaction。

    5. 正确使用Eager和Lazy能显著提交效率。

    6. 使用persist,只需注意操作的对象要么是new,要么是受管的,否则肯定出问题。

    7. 使用merge,基本上无论如何都不会抛异常,但后续的操作必须针对该方法返回的新受管对象进行操作,否则肯定出问题。

    四、参考资料

    1. http://www.oracle.com/technology/products/ias/toplink/jpa/index.html

    2. http://baike.baidu.com/view/1036852.htm?fr=ala0

    3. http://java.sun.com/javaee/technologies/persistence.jsp

    4. http://pz0513.blog.51cto.com/443986/113098

    ==本文谢绝转载,谢谢==

  • 2 Comments
  • Filed under: 周主题
  • JTable Print

    几天在做关于JTable的打印工作,项目中客户需要直接将JTable打印出来,之前这方面工作做的比较少,经过这几天的研究,稍微有些积累,现在分享一下。

    JTable打印目前用的比较多的还是JasperReport来实现的,JasperReport开源免费,但是不是特别好用,这也是为什么JasperReport免费用,但是培训是收费的。

    不过常用的一些JTable的打印实现起来也还好了,只是复杂的稍微有些麻烦,可以看看我之前介绍的一篇入门文章:JasperReport 个人使用的一些经验

    这里主要分享的是通过Swing 打印的积累,JasperReport常用的简单的打印没问题,但是遇上比较复杂的表单,比如合成表头等,就稍微有点麻烦,当然也有可能是自己没有找到JasperReport的处理方法,要是有朋友知道欢迎分享下。

    通过Swing来绘制,应该是无奈的一步,不过通过这无奈的一步,和同事的帮助下,却学习到了不少这方面的知识,尤其是Swing绘制方面的。

    下面的代码是我抽出来的一个比较通用的,基于TableModel的表格打印,普通表头,不带合成表头的,如果Table Model不一样,那么自己就需要改改了。

    import java.awt.Font;
    import java.awt.FontMetrics;
    import java.awt.Graphics;
    import java.awt.print.PageFormat;
    import java.awt.print.Printable;
    import java.awt.print.PrinterException;
    import java.awt.print.PrinterJob;
    import java.math.BigDecimal;
    import java.text.NumberFormat;
    import javax.swing.JTable;
    import javax.swing.table.TableModel;
    
    /**
     *
     * @author xiaoquan
     */
    public class SwingCommonPrinitTools implements Printable {
    
        private TableModel model = null;
        private String info;
        private int totalRow = 0;
        private static final int LEFT = 0;
        private static final int RIGHT = 1;
        private static final int CENTER = 2;
        private static final int AUTO = 3;
    
        public void printTable(TableModel model,
                String info) {
            this.model = model;
            this.info = info;
            totalRow = model.getRowCount();
            PrinterJob printJob = PrinterJob.getPrinterJob();
            printJob.setPrintable(this);
            if (printJob.printDialog()) {
                try {
                    printJob.print();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        private static final double paper_offset_x = 20;
        private static final double paper_offset_y = 20;
        private static final double title_time_margin = 10;
        private static final double time_body_margin = 2;
        private static final double cell_padding_y = 3;
        private static final double cell_padding_x = 2;
        private static final double body_btm_margin = 20;
        private static final double body_cell_height = 20;
        private static final Font title_font = new Font("黑体", Font.PLAIN, 18);
        private static final Font time_font = new Font("Dialog", Font.PLAIN, 10);
        private static final Font body_font = new Font("Dialog", Font.PLAIN, 10);
    
        @Override
        public int print(Graphics g, PageFormat pf, int pageIndex) throws
                PrinterException {
            //纸张宽
            double pageWidth = pf.getImageableWidth();
            //纸张高
            double pageHeight = pf.getImageableHeight();
            //打印的内容起始X
            double pageStartX = pf.getImageableX();
            //打印的内容起始Y
            double pageStartY = pf.getImageableY();
    
            //表头高
            double tableHeadH = 0;
            //Cell高
            double cellH = 0;
    
            //计算表头高度和单元格高度
            g.setFont(body_font);
            FontMetrics cellFm = g.getFontMetrics();
            cellH = cellFm.getHeight() + cell_padding_y * 2 + 1;
            tableHeadH = cellH * 2;
    
            //计算Title以及其位置
            String title = info;
            g.setFont(title_font);
            FontMetrics titleFm = g.getFontMetrics();
            int titleW = titleFm.stringWidth(title);
    
            //表底和表头文字属性
            g.setFont(time_font);
            FontMetrics btmFm = g.getFontMetrics();
            FontMetrics timeFm = g.getFontMetrics();
    
            //表格以上的Margin
            double tableTopMargin = paper_offset_y + titleFm.getHeight() +
                    title_time_margin + timeFm.getHeight() + time_body_margin;
    
            //表格每列的最大宽度
            double[] cellColMaxWidths = caculateTableCellWidth(model, cellFm);
    
            //当前Page的数据容量高度-不包括表头和表尾
            double currentPageDataCapacityHeight = pageHeight - tableTopMargin -
                    tableHeadH - btmFm.getHeight() - body_btm_margin - 1;
    
            //当前Page的数据容量
            int currentPageBodyCapacityRows = (int) (currentPageDataCapacityHeight /
                    cellH);
    
            //Y方向的分页数量
            int pagesY = 0;
            if (model.getRowCount() % currentPageBodyCapacityRows == 0) {
                pagesY = (int) (model.getRowCount() /
                        currentPageBodyCapacityRows);
            } else {
                pagesY = (int) (model.getRowCount() /
                        currentPageBodyCapacityRows) +
                        1;
            }
    
            //当前页数大于总页数时不打印
            if (pageIndex + 1 > pagesY) {
                return NO_SUCH_PAGE;
            }
    
            //绘制Title
            g.setFont(title_font);
            g.drawString(title, (int) (pageStartX +
                    (pageWidth - titleW) / 2), (int) (pageStartY +
                    paper_offset_y +
                    titleFm.getAscent()));
    
            //绘制区域移动到新的(0,0)点
            g.translate((int) (paper_offset_x + pageStartX), (int) (tableTopMargin +
                    pageStartY));
            int currentX = 0, currentY = 0;
    
            //绘制第一张表
    
            //绘制表头
            g.setFont(time_font);
            String time = "表头: " + info;
            g.drawString(time, currentX, currentY);
            currentY += 5;
            //绘制单一表头
            for (int i = 0; i < model.getColumnCount(); i++) {
                double width = cellColMaxWidths[i];
                double height = tableHeadH;
                String name = model.getColumnName(i);
                drawCell(g, name, currentX, currentY, (int) width,
                        (int) height, CENTER);
                currentX += width;
            }
    
            //绘制数据
            currentX = 0;
            currentY = (int) tableHeadH;
            //当前Page的数据容量
            int rightCellX = 0;
            int yIndex = pageIndex;
            int startRow = currentPageBodyCapacityRows * yIndex;
            int endRow = (currentPageBodyCapacityRows * (yIndex + 1)) >
                    totalRow
                    ? totalRow
                    : (currentPageBodyCapacityRows * (yIndex + 1));
            for (int row = startRow; row < endRow; row++) {
                //绘制单项表头下面的数据
                for (int i = 0; i < model.getColumnCount(); i++) {
                    double width = cellColMaxWidths[i];
                    double height = body_cell_height;
                    Object value = model.getValueAt(row, i);
                    drawCell(g, value, currentX, currentY, (int) width,
                            (int) height, AUTO);
                    currentX += width;
                    rightCellX = currentX;
                }
                currentX = 0;
                currentY += cellH;
            }
    
            //绘制闭合线,下面和右侧两条
            g.drawLine(currentX, currentY, rightCellX, currentY);
            g.drawLine(rightCellX, 5, rightCellX, currentY);
    
            drawBottomInfo(pageIndex, pagesY, currentY, g, (int) pageWidth);
            return PAGE_EXISTS;
        }
    
        private void drawBottomInfo(int pageIndex, int pagesY,
                int currentY, Graphics g, int pageWidth) {
            if (pageIndex + 1 == pagesY) {
                //绘制底部信息
                int btmX = 0;
                int btmY = currentY + 20;
                g.drawString("负责人:", btmX, btmY);
                g.drawString("制表:", pageWidth / 3, btmY);
                FontMetrics fm = g.getFontMetrics();
                int dataWidth = fm.stringWidth("日期: 2009/10/26");
                g.drawString("日期:", pageWidth - dataWidth, btmY);
            }
        }
    
        /**
         * 计算最大列宽
         * @param cellFm
         * @return
         */
        private double[] caculateTableCellWidth(
                TableModel model,
                FontMetrics cellFm) {
            //表格每列的最大宽度
            double[] cellColMaxWidths = new double[model.getColumnCount()];
    
            //计算表头每列最大宽度
            double[] headerColMaxWidths = new double[model.getColumnCount()];
    
            for (int i = 0; i < model.getColumnCount(); i++) {
                String name = model.getColumnName(i);
                headerColMaxWidths[i] = cellFm.stringWidth(name) + cell_padding_x *
                        2 + 1;
            }
            //没有数据时,表头每列的最大宽度就是表格每列的最大宽度
            cellColMaxWidths = headerColMaxWidths;
    
            //算数据每列的最大宽度和表头每列最大宽度对比
            for (int j = 0; j < model.getRowCount(); j++) {
                for (int i = 0; i < model.getColumnCount(); i++) {
                    //做些数据类型的判断
                    Object value = model.getValueAt(j, i);
                    if (value instanceof BigDecimal) {
                        value = ((BigDecimal) value).doubleValue();
                    }
                    String text = "";
                    if (value != null) {
                        text = value.toString();
                    }
                    double temp = cellFm.stringWidth(text) + cell_padding_x * 2 + 1;
                    if (cellColMaxWidths[i] < temp) {
                        cellColMaxWidths[i] = temp;
                    }
                }
            }
            return cellColMaxWidths;
        }
    
        /**
         * 绘制单元格及里面的文字
         * @param g
         * @param value
         * @param x
         * @param y
         * @param width
         * @param height
         */
        private static void drawCell(Graphics g, Object value, int x, int y,
                int width,
                int height, int locate) {
    
            g.drawLine(x, y, x + width - 1, y);
            g.drawLine(x, y, x, y + height - 1);
            FontMetrics fm = g.getFontMetrics();
            if (value == null) {
                value = "";
            }
            switch (locate) {
                case 0:
                    //居左
                    g.drawString(value.toString(), (int) (x + cell_padding_x), y +
                            (height - fm.getHeight()) / 2 + fm.getAscent());
                case 1:
                    //居右
                    g.drawString(value.toString(),
                            (int) (x +
                            (width - fm.stringWidth(value.toString()) + width -
                            fm.stringWidth(value.toString()) - cell_padding_x) /
                            2), y +
                            (height - fm.getHeight()) / 2 + fm.getAscent());
                case 2:
                    //居中
                    g.drawString(value.toString(), x + (width - fm.stringWidth(
                            value.toString())) / 2, y + (height -
                            fm.getHeight()) / 2 + fm.getAscent());
                case 3:
                    //自动判断
                    NumberFormat formatter = NumberFormat.getNumberInstance();
                    formatter.setMinimumFractionDigits(2);
                    formatter.setMaximumFractionDigits(2);
                    //根据数据类型左对齐还是右对齐绘制还是居中对齐
                    if (value instanceof BigDecimal) {
                        //居右
                        value = ((BigDecimal) value).doubleValue();
                        value = formatter.format(value);
                        g.drawString(value.toString(),
                                (int) (x +
                                (width - fm.stringWidth(value.toString()) + width -
                                fm.stringWidth(value.toString()) - cell_padding_x) /
                                2), y +
                                (height - fm.getHeight()) / 2 + fm.getAscent());
                    } else if (value instanceof Integer || value instanceof Long ||
                            value instanceof Double) {
                        //居右
                        g.drawString(value.toString(),
                                (int) (x +
                                (width - fm.stringWidth(value.toString()) + width -
                                fm.stringWidth(value.toString()) - cell_padding_x) /
                                2), y +
                                (height - fm.getHeight()) / 2 + fm.getAscent());
                    } else {
                        //居中
                        g.drawString(value.toString(), x + (width - fm.stringWidth(
                                value.toString())) / 2, y + (height -
                                fm.getHeight()) / 2 + fm.getAscent());
                    }
            }
        }
    
        public static void main(String[] args) {
            new SwingCommonPrinitTools().printTable(testData(), "测试");
        }
    
        private static TableModel testData() {
            final Object rows[][] = {
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", "Test1", "Test2", "Test3"},
                {"five", "go - \u4E94", "Test1", "Test2", "Test3"},
                {"six", "roku - \u516D", "Test1", "Test2", "Test3"},
                {"seven", "shichi - \u4E03", "Test1", "Test2", "Test3"},
                {"eight", "hachi - \u516B", "Test1", "Test2", "Test3"},
                {"nine", "kyu - \u4E5D", "Test1", "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", "Test1", "Test2", "Test3"},
                {"five", "go - \u4E94", "Test1", "Test2", "Test3"},
                {"six", "roku - \u516D", "Test1", "Test2", "Test3"},
                {"seven", "shichi - \u4E03", "Test1", "Test2", "Test3"},
                {"eight", "hachi - \u516B", "Test1", "Test2", "Test3"},
                {"nine", "kyu - \u4E5D", "Test1", "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", "Test1", "Test2", "Test3"},
                {"five", "go - \u4E94", "Test1", "Test2", "Test3"},
                {"six", "roku - \u516D", "Test1", "Test2", "Test3"},
                {"seven", "shichi - \u4E03", "Test1", "Test2", "Test3"},
                {"eight", "hachi - \u516B", "Test1", "Test2", "Test3"},
                {"nine", "kyu - \u4E5D", "Test1", "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", "Test1", "Test2", "Test3"},
                {"five", "go - \u4E94", "Test1", "Test2", "Test3"},
                {"six", "roku - \u516D", "Test1", "Test2", "Test3"},
                {"seven", "shichi - \u4E03", "Test1", "Test2", "Test3"},
                {"eight", "hachi - \u516B", "Test1", "Tes12121t2", "Test3"},
                {"nine", "kyu - \u4E5D", "Test1", "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", 12, "Test2", "Test3"},
                {"five", "go - \u4E94", 121212, "Test2", "Test3"},
                {"six", "roku - \u516D", 1212121212, "Test2", "Test3"},
                {"seven", "shichi - \u4E03", 12.01, "Test2", "Test3"},
                {"eight", "hachi - \u516B", 135.12, "Test2", "Test3"},
                {"nine", "kyu - \u4E5D", 93828.34, "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},
                {"one", "ichi - \u4E00", "Test1", "Test2", "Test3"},
                {"two", "ni - \u4E8C", "Test1", "Test2", "Test3"},
                {"three", "san - \u4E09", "Test1", "Test2", "Test3"},
                {"four", "shi - \u56DB", "Test1", "Test2", "Test3"},
                {"five", "go - \u4E94", "Test1", "Test2", "Test3"},
                {"six", "roku - \u516D", "Test1", "Test2", "Test3"},
                {"seven", "shichi - \u4E03", "Test1", "Test2", "Test3"},
                {"eight", "hachi - \u516B", "Test1", "Test2", "T1212121212est3"},
                {"nine", "kyu - \u4E5D", "Test1", "Test2", "Test3"},
                {"ten", "ju - \u5341", "Test1", "Test2", "Test3"},};
            final Object headers[] = {"English", "Japanese", "Column1", "Column2",
                "Column3"};
            JTable table = new JTable(rows, headers);
            return table.getModel();
        }
    }
    
  • 3 Comments
  • Filed under: 技术
  • 提是保证你的Linux已经安装了相应的Java软件,如果没有安装Java, 先装安装Java,配置Java环境, 假如安装到了/usr/java/jre1.6.0_17/

    找到你的FireFox安装的目录,假如安装到了 /usr/firefox

    cd /usr/firfox

    如果有plugins目录那就继续往下看,如果没有,mkdir plugins

    ln -s /usr/java/jre1.6.0_17/plugin/i386/ns7/libjavaplugin_oji.so

    OK,至此大功告成,测试一下,打开 http://www.java.com/zh_CN/download/installed.jsp 看看即可

    ubuntu下安装这个其实很方便,firefox会自动检测并让你安装,java plugin, 但是在有些Linux环境下就需要手动了。

  • 0 Comments
  • Filed under: 技术
  • 今天在优化功能时, 想直接将文件保存到桌面, 于是开始查找相关代码.

    FileSystemView 是 JFileChooser 的文件系统网关。由于 JDK1.1 File API 不允许对诸如根分区、文件类型信息或隐藏文件位之类的信息进行访问,此类被设计成能够直接获得尽可能多的特定于 OS 的文件系统信息。

    在JFileChooser对象中添加如下代码即可

    fc.setCurrentDirectory(FileSystemView.getFileSystemView().getHomeDirectory());
    
  • 0 Comments
  • Filed under: 技术
  • 关于Twitter这里就不多介绍了, 基本上能看到这篇文章的都用过的, 当然不知道也没关系, Google一下.由于Twitter的API 开放性, 所以其应用现在可以用多如牛毛也描述了, 各种各样的, 本文基于Java, 通过twitter API来获取twitter消息, 并以PDF的形式进行归档.准备工具:

    1. HttpClient 3.x 库文件, 通过此库来从Twitter API获取消息, 同时需要 Comomons LoggingCodec 两个库文件. 请自行下载.

    2. dom4j 库文件, 通过此库来解析Twitter的消息XML和获取中间的数据.

    3. iText 库文件, 用来创建PDF文档.


    每个Twitter应用都是基于twitter API来实现的, 有关Twitter API的使用可以看这里: Twitter API wiki(官方的,需要翻墙), 这里我们假设你只对获取数据感兴趣, 同时想以某种方式显示他们, 至于其他功能, 相信通过本教程的介绍, 你可以自己去钻研了.从TweetConsumer开始:

    import org.dom4j.Element;
    public interface TweetConsumer {
        public String tweet(Element element) throws TweetException;
    }
    

    Twitter API提供XML, JSON 和RSS几种格式, 这里我们选择XML格式, 通过dom4j来解析, 上面是一个叫做TweetConsumer的借口, 作用就是来处理消息的, 其实现在后面会讲到.

    自定义一个消息异常, 当出现问题时抛出:

    public class TweetException extends Exception {
        private static final long serialVersionUID = 7577136074623618615L;
        public TweetException(Exception e) {
            super(e);
        }
    }
    

    接下来是本文的重点, 通过Twitter API去获取消息:

    public class TweetProducer {
        protected HttpClient client;
        protected TweetConsumer consumer;
    
        public void createClient(String username, String password) {
            client = new HttpClient();
            client.getState().setCredentials(
                //由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:xiao-quan.appspot.com
                new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),
                new UsernamePasswordCredentials(username, password));
            client.getParams().setAuthenticationPreemptive(true);
        }
    
        public void setConsumer(TweetConsumer consumer) {
            this.consumer = consumer;
        }
    
        public String execute(String since_id) throws TweetException {
            String id = null;
            String tmp;
            //由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:http://xiao-quan.appspot.com/api/statuses/friends_timeline.xml
            GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
            if (since_id != null &amp;&amp; since_id.length() > 0) {
                get.setQueryString("?count=200&amp;since_id=" + since_id);
            }
            else {
                get.setQueryString("?count=200");
            }
            get.setDoAuthentication(true);
            try {
                client.executeMethod(get);
                SAXReader reader = new SAXReader();
                Document document = reader.read(get.getResponseBodyAsStream());
                Element root = document.getRootElement();
                for (Iterator i = root.elementIterator(); i.hasNext(); ) {
                    tmp = consumer.tweet((Element)i.next());
                    if (id == null) id = tmp;
                }
            } catch (HttpException e) {
                throw new TweetException(e);
            } catch (IOException e) {
                throw new TweetException(e);
            } catch (DocumentException e) {
                throw new TweetException(e);
            } finally {
                get.releaseConnection();
            }
            return id;
        }
    }
    

    TweetProducer 里面有三个方法:
    createClient(), 创建HttpClient对象, 通过HttpClient来连接Twitter API, 由于Twitter使用了HTTP Basic Authentication,
    所以你需要提供你的twitter用户名和密码来登录, 获取你的朋友们的消息.
    setConsumer(), 传一个TweetConsumer接口给TweetProducer, 我们有多种输出方案, 当然这里要通用点.
    excute(), 这是才是做事情的地方, 获取哪个消息ID以后的消息, 返回一个消息ID.

    获取消息:

    GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
    

    Twitter API默认获取的消息条数是20, 最大200, 你可以自己定义获取的数量, 同时也可以通过一些参数来指定获取第几页的消息等, 具体使用参考Twitter API WIKI上的定义.

    例如, 获取消息ID为since_id以后的200个消息:

    get.setQueryString("?count=200&since_id=" + since_id);
    

    设置完查询条件后, 执行:

    client.executeMethod(get);
    

    将返回的信息传给SAXReader,以便获取消息root:

    SAXReader reader = new SAXReader();
    Document document = reader.read(get.getResponseBodyAsStream());
    Element root = document.getRootElement();
    

    最后迭代每个带 标签的Element:

    for (Iterator i = root.elementIterator(); i.hasNext(); ) {
        tmp = consumer.tweet((Element)i.next());
        if (id == null) id = tmp;
    }
    

    由于Twitter的消息列表是按时间降序排列, 那么第一个消息就是最新的消息了, 这个消息ID就是excute()要返回的String.

    获取到消息后, 接下来我们就输出消息:

    import org.dom4j.Element;
    
    public class SystemOutTweets implements TweetConsumer {
        public String tweet(Element tweet) {
            System.out.println(tweet.asXML());
            return tweet.elementText("id");
        }
    
        public static void main(String[] args) throws TweetException {
            TweetProducer t = new TweetProducer();
            t.createClient("gavinquan", "123456");
            t.setConsumer(new SystemOutTweets());
            System.out.println("Start tweeting:");
            String id = t.execute(null);
            System.out.println("The End; last tweet: " + id);
        }
    }
    

    将上面的介绍的代码整理好, 并执行, 就会在控制台输出, XML格式的消息列表, 如下是其中的一个片段:

    <status>
      <created_at>Wed Sep 09 06:07:04 +0000 2009</created_at>
      <id>3858278078</id>
      <text>gmail服务器错误了?</text>
      <source><a href="http://twitterfox.net/" rel="nofollow">TwitterFox</a></source>
      <truncated>false</truncated>
      <in_reply_to_status_id/>
      <in_reply_to_user_id/>
      <favorited>false</favorited>
      <in_reply_to_screen_name/>
      <user>
        <id>10415752</id>
        <name>hugege</name>
        <screen_name>hugege</screen_name>
        <location>Shantou Guangdong China </location>
        <description>www.gegehost.com 博客主机服务</description>
        <profile_image_url>http://a1.twimg.com/profile_images/329549706/hugegelogo_normal.jpg</profile_image_url>
        <url>http://hugege.com/</url>
        <protected>false</protected>
        <followers_count>1607</followers_count>
        <profile_background_color>C6E2EE</profile_background_color>
        <profile_text_color>663B12</profile_text_color>
        <profile_link_color>1F98C7</profile_link_color>
        <profile_sidebar_fill_color>DAECF4</profile_sidebar_fill_color>
        <profile_sidebar_border_color>C6E2EE</profile_sidebar_border_color>
        <friends_count>1335</friends_count>
        <created_at>Tue Nov 20 16:00:58 +0000 2007</created_at>
        <favourites_count>3</favourites_count>
        <utc_offset>28800</utc_offset>
        <time_zone>Beijing</time_zone>
        <profile_background_image_url>http://s.twimg.com/a/1252448032/images/themes/theme2/bg.gif</profile_background_image_url>
        <profile_background_tile>false</profile_background_tile>
        <statuses_count>1540</statuses_count>
        <notifications>false</notifications>
        <verified>false</verified>
        <following>false</following>
      </user>
    </status>
    

    同时会输出, 你的最后一个消息的消息ID:

    The End; last tweet: 3858279409
    

    当然你也可以直接通过这个ID, 来去获取你的该ID以后的消息:

    t.execute("3858279409");
    

    OK, 到这里, 我们的任务已经过去一半多了, 获取twitter消息, 那么接下来的内容, 我们来看如何将消息以PDF的形式进行归档.

    通过iText生成PDF, 需要5步, 请看下面的Demo :

    import java.io.FileOutputStream;
    import com.lowagie.text.Document;
    import com.lowagie.text.Paragraph;
    import com.lowagie.text.pdf.PdfWriter;
    
    public class HelloWorld {
        public static void main(String args[]) {
            try {
                // step 1: create a Document object
                Document document = new Document();
                // step 2: connect the Document with an OutputStream using a PdfWriter
                PdfWriter.getInstance(document, new FileOutputStream("hello.pdf"));
                // step 3: open the document
    
                document.open();
                // step 4: add content
                document.add(new Paragraph("Hello World"));
                // step 5: close the document
                document.close();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
    

    大体工作流程就如上面所说,相信都可以看懂, OK, 下面我就直接给出代码, 重点地方提一下.

    PDFWriter类

    import com.lowagie.text.Document;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.Font;
    import com.lowagie.text.PageSize;
    import com.lowagie.text.Phrase;
    import com.lowagie.text.Rectangle;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfPTable;
    import com.lowagie.text.pdf.PdfWriter;
    import com.quanlei.exception.TweetException;
    import com.quanlei.twitter.TweetConsumer;
    import com.quanlei.twitter.TweetProducer;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class PdfTwitter implements TweetConsumer {
    
        private int counter = 0;
        private Document document;
        private Font small_white = new Font(Font.HELVETICA, 8, Font.BOLD);
        private Font bold = new Font(Font.HELVETICA, 11, Font.BOLD);
        private BaseFont bfChinese = BaseFont.createFont("STSong-Light",
                "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        private Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
        private SimpleDateFormat simpleDateTimeFormat =
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        public PdfTwitter() throws DocumentException, IOException {
            document = new Document(PageSize.A4, 72, 36, 36, 36);
    
            PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(
                    "world.pdf"));
            writer.setPageEvent(new TwitterPage());
            document.open();
        }
    
        public void close() {
            document.close();
        }
    
        public String tweet(org.dom4j.Element tweet) throws TweetException {
            try {
                org.dom4j.Element user = tweet.element("user");
                PdfPTable table = new PdfPTable(new float[]{1, 10, 12});
                table.setTableEvent(new TableBackground());
    
                table.setWidthPercentage(100);
                table.setSpacingBefore(8);
                table.getDefaultCell().setPadding(5);
                table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
                table.addCell("");
                table.addCell(new Phrase(user.elementText("screen_name"),
                        small_white));
                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
    
                table.addCell(new Phrase(simpleDateTimeFormat.format(
                        new Date(tweet.elementText("created_at"))),
                        small_white));
                table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
                table.getDefaultCell().setFixedHeight(38);
                table.addCell(new Phrase(String.valueOf(++counter), small_white));
                table.getDefaultCell().setColspan(2);
                Phrase p = new Phrase();
                p.add(new Phrase(user.elementText("name"), FontChinese));
                p.add(new Phrase(": ", bold));
                p.add(new Phrase(tweet.elementText("text"), FontChinese));
                table.addCell(p);
                document.add(table);
                return tweet.elementText("id");
            } catch (DocumentException e) {
                e.printStackTrace();
                throw new TweetException(e);
            } catch (IOException e) {
                e.printStackTrace();
                throw new TweetException(e);
            }
        }
    
        public static void main(String[] args)
                throws DocumentException, org.dom4j.DocumentException,
                FileNotFoundException, IOException, TweetException {
            PdfTwitter pdf = new PdfTwitter();
            TweetProducer t = new TweetProducer();
            t.createClient("gavinquan", "123456");
            t.setConsumer(pdf);
            t.execute(null);
            pdf.close();
        }
    }
    

    上面的类已经可以输出PDF了, 但是不是很美观, 加点修饰, 每个Twitter消息一个框, 这是背景:

    import java.io.IOException;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfContentByte;
    import com.lowagie.text.pdf.PdfPTable;
    import com.lowagie.text.pdf.PdfPTableEvent;
    
    public class TableBackground implements PdfPTableEvent {
    
        protected BaseFont bf;
    
        public TableBackground() throws DocumentException, IOException {
            bf = BaseFont.createFont(BaseFont.TIMES_BOLDITALIC, BaseFont.WINANSI,
                    BaseFont.NOT_EMBEDDED);
        }
    
        public void tableLayout(PdfPTable table, float[][] width, float[] height,
                int headerRows, int rowStart, PdfContentByte[] canvas) {
            PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
            cb.saveState();
            cb.setRGBColorFill(0x9a, 0xe4, 0xe8);
            cb.roundRectangle(width[0][0], height[2], width[0][3] - width[0][0],
                    height[0] - height[2], 4);
            cb.roundRectangle(width[0][1], height[2] + 3,
                    width[0][3] - width[0][1] - 3, height[1] - height[2] - 6, 4);
            cb.eoFill();
            cb.beginText();
            cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
            cb.setFontAndSize(bf, 28);
            cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            cb.showTextAligned(Element.ALIGN_LEFT, "\"", width[0][1],
                    height[1] - 10, 0);
            cb.endText();
            cb.restoreState();
        }
    }
    

    下面再画蛇添足, 加点外围修饰, 每个PDF页面左侧加上文字, 算是起点宣传作用:

    import java.io.IOException;
    import com.lowagie.text.Document;
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.Element;
    import com.lowagie.text.pdf.BaseFont;
    import com.lowagie.text.pdf.PdfContentByte;
    import com.lowagie.text.pdf.PdfPageEventHelper;
    import com.lowagie.text.pdf.PdfWriter;
    
    public class TwitterPage extends PdfPageEventHelper {
    
        protected BaseFont bf;
    
        public TwitterPage() throws DocumentException, IOException {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.NOT_EMBEDDED);
        }
    
        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            PdfContentByte cb = writer.getDirectContent();
            cb.saveState();
            cb.beginText();
            cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
            cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
            cb.setFontAndSize(bf, 18);
            cb.showTextAligned(Element.ALIGN_RIGHT, "www.quanlei.com", 68, 806, 90);
            cb.endText();
            cb.restoreState();
        }
    }
    

    最后配个截图, 为本文画上句号, 注, 由于图缩略了, 效果有点虚了:

    PDF效果图

    英文原版出处: http://www.javaworld.com/javaworld/jw-03-2009/jw-03-write-your-own-twitter-app.html , 本文抽取了原英文的重点, 并配以文字介绍,加工而成.

  • 0 Comments
  • Filed under: 技术
  •